parent
530500b2c0
commit
4f9d832a39
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.9 (Ant)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@ -0,0 +1,12 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="E302" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (Ant)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/billiards.iml" filepath="$PROJECT_DIR$/.idea/billiards.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,20 @@
|
||||
import abc
|
||||
|
||||
import pygame
|
||||
|
||||
from config.sys import *
|
||||
|
||||
|
||||
class DrawableItem(metaclass=abc.ABCMeta):
|
||||
|
||||
@abc.abstractmethod
|
||||
def draw(self, window: pygame.Surface):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _get_relative_pos(pos: tuple[float, float]) -> tuple[float, float]:
|
||||
return (SCREEN_WIDTH - TABLE_WIDTH) / 2 + pos[0], (SCREEN_HEIGHT - TABLE_HEIGHT) / 2 + pos[1]
|
||||
|
||||
@staticmethod
|
||||
def _get_table_pos(pos: tuple[float, float]) -> tuple[float, float]:
|
||||
return pos[0] - (SCREEN_WIDTH - TABLE_WIDTH) / 2, pos[1] - (SCREEN_HEIGHT - TABLE_HEIGHT) / 2
|
||||
@ -0,0 +1,63 @@
|
||||
import pygame.draw
|
||||
|
||||
from common.drawableItem import DrawableItem
|
||||
from enums.Color import BLACK
|
||||
from enums.category import Category
|
||||
from config.sys import *
|
||||
import pymunk
|
||||
|
||||
|
||||
class BaseBilliard(DrawableItem):
|
||||
def __init__(self, score: int, category: Category, color: tuple[int, int, int], pos=(0, 0)):
|
||||
self.__score = score
|
||||
self.__category = category
|
||||
self.__color = color
|
||||
self.__rigid_body = pymunk.Body(BALL_MASS, 1)
|
||||
self.__rigid_body.position = pos
|
||||
self.__shape = pymunk.Circle(self.__rigid_body, BALL_RADIUS)
|
||||
self.__shape.elasticity = 1.0
|
||||
|
||||
@property
|
||||
def score(self):
|
||||
return self.__score
|
||||
|
||||
@property
|
||||
def category(self):
|
||||
return self.__category
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
return self.__color
|
||||
|
||||
@property
|
||||
def rigid_body(self):
|
||||
return self.__rigid_body
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
return self.__shape
|
||||
|
||||
@property
|
||||
def pos(self):
|
||||
return self.__rigid_body.position
|
||||
|
||||
@pos.setter
|
||||
def pos(self, pos: tuple[float, float]):
|
||||
self.__rigid_body.position = pos
|
||||
|
||||
def draw(self, window):
|
||||
pygame.draw.circle(window, self.__color, self._get_relative_pos(self.pos), BALL_RADIUS)
|
||||
pygame.draw.circle(window, BLACK, self._get_relative_pos(self.pos), BALL_RADIUS, 1)
|
||||
pass
|
||||
|
||||
def resistance(self):
|
||||
length = self.__rigid_body.velocity.length
|
||||
if length == 0:
|
||||
return
|
||||
if length < TABLE_FRICTION_FACTOR / FPS:
|
||||
self.__rigid_body.velocity = pymunk.Vec2d.zero()
|
||||
return
|
||||
v = self.__rigid_body.velocity.scale_to_length(length - TABLE_FRICTION_FACTOR / FPS)
|
||||
if v.length <= 1:
|
||||
v = (0, 0)
|
||||
self.__rigid_body.velocity = v
|
||||
@ -0,0 +1,43 @@
|
||||
import pygame
|
||||
import pymunk
|
||||
|
||||
from common.drawableItem import DrawableItem
|
||||
from common.game.billiards.baseBilliard import BaseBilliard
|
||||
from config.sys import CUE_LENGTH, CUE_WIDTH, BALL_RADIUS
|
||||
from enums.Color import BLACK, WHITE
|
||||
|
||||
|
||||
class Cue(DrawableItem):
|
||||
def __init__(self, cue_ball: BaseBilliard):
|
||||
self.__cue_ball = cue_ball
|
||||
self.__mouse_pos: tuple[float, float] = (0, 0)
|
||||
pass
|
||||
|
||||
def draw(self, window: pygame.Surface):
|
||||
if not self.__cue_ball:
|
||||
return
|
||||
if self.__cue_ball.rigid_body.velocity != pymunk.Vec2d.zero():
|
||||
print(self.__cue_ball.rigid_body.velocity)
|
||||
return
|
||||
cue_ball_pos = self.__cue_ball.pos
|
||||
mouse_pos = pymunk.Vec2d(self.__mouse_pos[0], self.__mouse_pos[1])
|
||||
|
||||
cue_vector_x = (self._get_relative_pos(cue_ball_pos) - mouse_pos)[0]
|
||||
cue_vector_y = (self._get_relative_pos(cue_ball_pos) - mouse_pos)[1]
|
||||
cue_vector = pymunk.Vec2d(cue_vector_x, cue_vector_y).scale_to_length(CUE_LENGTH)
|
||||
|
||||
pygame.draw.line(window, WHITE, self._get_relative_pos(cue_ball_pos),
|
||||
mouse_pos - (mouse_pos - self._get_relative_pos(cue_ball_pos)).scale_to_length(BALL_RADIUS), 1)
|
||||
pygame.draw.circle(window, WHITE, mouse_pos, BALL_RADIUS, 1)
|
||||
|
||||
pygame.draw.line(window, BLACK, self._get_relative_pos(cue_ball_pos),
|
||||
self._get_relative_pos(cue_ball_pos) + cue_vector, CUE_WIDTH)
|
||||
pass
|
||||
|
||||
@property
|
||||
def mouse_pos(self):
|
||||
return self.__mouse_pos
|
||||
|
||||
@mouse_pos.setter
|
||||
def mouse_pos(self, mouse_pos):
|
||||
self.__mouse_pos = mouse_pos
|
||||
@ -0,0 +1,27 @@
|
||||
import pygame
|
||||
import pymunk
|
||||
|
||||
from common.drawableItem import DrawableItem
|
||||
from common.game.billiards.baseBilliard import BaseBilliard
|
||||
from config.sys import HOLE_RADIUS, BALL_RADIUS
|
||||
from enums.Color import GARY
|
||||
|
||||
|
||||
class Hole(DrawableItem):
|
||||
|
||||
def __init__(self, pos):
|
||||
self.__pos = pos
|
||||
pass
|
||||
|
||||
def draw(self, window: pygame.Surface):
|
||||
pygame.draw.circle(window, GARY, self._get_relative_pos(self.__pos), HOLE_RADIUS)
|
||||
|
||||
def goal(self, space: pymunk.Space, balls: list[BaseBilliard]):
|
||||
for _ in balls:
|
||||
pos = pygame.Vector2(_.rigid_body.position.x - self.__pos[0], _.rigid_body.position.y - self.__pos[1])
|
||||
if pos.length() <= HOLE_RADIUS:
|
||||
_.rigid_body.position = pymunk.Vec2d(-BALL_RADIUS, -BALL_RADIUS)
|
||||
space.remove(_.rigid_body)
|
||||
balls.remove(_)
|
||||
pass
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
import pygame.draw
|
||||
import pymunk
|
||||
|
||||
from common.drawableItem import DrawableItem
|
||||
from common.game.hole import Hole
|
||||
from config.sys import *
|
||||
from enums.Color import BLACK, GREEN
|
||||
|
||||
|
||||
class Table(DrawableItem):
|
||||
def __init__(self):
|
||||
self.__size = TABLE_SIZE
|
||||
self.__cushing_body = [
|
||||
pymunk.Body(body_type=pymunk.Body.STATIC),
|
||||
pymunk.Body(body_type=pymunk.Body.STATIC),
|
||||
pymunk.Body(body_type=pymunk.Body.STATIC),
|
||||
pymunk.Body(body_type=pymunk.Body.STATIC),
|
||||
pymunk.Body(body_type=pymunk.Body.STATIC),
|
||||
pymunk.Body(body_type=pymunk.Body.STATIC)
|
||||
]
|
||||
self.__cushing_shape: list[pymunk.Segment] = [
|
||||
pymunk.Segment(self.__cushing_body[0], (0, HOLE_RADIUS), (0, TABLE_HEIGHT - HOLE_RADIUS), 1),
|
||||
pymunk.Segment(self.__cushing_body[1], (HOLE_RADIUS, TABLE_HEIGHT),
|
||||
(TABLE_WIDTH / 2 - HOLE_RADIUS, TABLE_HEIGHT), 1),
|
||||
pymunk.Segment(self.__cushing_body[2], (TABLE_WIDTH / 2 + HOLE_RADIUS, TABLE_HEIGHT),
|
||||
(TABLE_WIDTH - HOLE_RADIUS, TABLE_HEIGHT), 1),
|
||||
pymunk.Segment(self.__cushing_body[3], (TABLE_WIDTH, TABLE_HEIGHT - HOLE_RADIUS),
|
||||
(TABLE_WIDTH, HOLE_RADIUS), 1),
|
||||
pymunk.Segment(self.__cushing_body[4], (HOLE_RADIUS, 0),
|
||||
(TABLE_WIDTH / 2 - HOLE_RADIUS, 0), 1),
|
||||
pymunk.Segment(self.__cushing_body[5], (TABLE_WIDTH / 2 + HOLE_RADIUS, 0),
|
||||
(TABLE_WIDTH - HOLE_RADIUS, 0), 1),
|
||||
]
|
||||
self.__holes = [
|
||||
Hole((0, 0)), Hole((TABLE_WIDTH / 2, 0)), Hole((TABLE_WIDTH, 0)),
|
||||
Hole((0, TABLE_HEIGHT)), Hole((TABLE_WIDTH / 2, TABLE_HEIGHT)), Hole((TABLE_WIDTH, TABLE_HEIGHT))
|
||||
]
|
||||
for _ in self.__cushing_shape:
|
||||
_.elasticity = 1.0
|
||||
pass
|
||||
|
||||
def draw(self, window):
|
||||
pos1 = self._get_relative_pos((0, 0))
|
||||
pos2 = (TABLE_WIDTH, TABLE_HEIGHT)
|
||||
pygame.draw.rect(window, GREEN, (pos1, pos2))
|
||||
for _ in self.__cushing_shape:
|
||||
pygame.draw.line(window, BLACK, self._get_relative_pos(_.a), self._get_relative_pos(_.b), TABLE_LINE_WIDTH)
|
||||
for _ in self.__holes:
|
||||
_.draw(window)
|
||||
pass
|
||||
|
||||
@property
|
||||
def cushing_body(self):
|
||||
return self.__cushing_body
|
||||
|
||||
@property
|
||||
def cushing_shape(self):
|
||||
return self.__cushing_shape
|
||||
|
||||
def goal(self, space, balls):
|
||||
for _ in self.__holes:
|
||||
_.goal(space, balls)
|
||||
@ -0,0 +1,14 @@
|
||||
BALL_RADIUS = 10.5
|
||||
BALL_MASS = 5
|
||||
|
||||
TABLE_SIZE = (TABLE_WIDTH, TABLE_HEIGHT) = (892.25, 444.5)
|
||||
HOLE_RADIUS = BALL_RADIUS * 1.8
|
||||
TABLE_LINE_WIDTH = 3
|
||||
TABLE_FRICTION_FACTOR = 60
|
||||
|
||||
CUE_WIDTH = 8
|
||||
CUE_LENGTH = 128
|
||||
|
||||
SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT) = (1200, 700)
|
||||
TITLE = 'Python Billiard'
|
||||
FPS = 60
|
||||
@ -0,0 +1,4 @@
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
GREEN = (0, 128, 0)
|
||||
GARY = (128, 128, 128)
|
||||
@ -0,0 +1,15 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Category(Enum):
|
||||
CUE_BALL = 0
|
||||
RED_BALL = 1
|
||||
YELLOW_BALL = 2
|
||||
GREEN_BALL = 3
|
||||
BROWN_BALL = 4
|
||||
BLUE_BALL = 5
|
||||
PINK_BALL = 6
|
||||
BLACK_BALL = 7
|
||||
COLORED_BALLS = [_ for _ in range(2, 8)]
|
||||
pass
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
import pygame
|
||||
import pymunk
|
||||
|
||||
from common import drawableItem
|
||||
from common.game.billiards.baseBilliard import BaseBilliard
|
||||
from common.game.cue import Cue
|
||||
from common.game.table import Table
|
||||
from config.sys import *
|
||||
from enums.Color import *
|
||||
from enums.category import *
|
||||
|
||||
|
||||
def new_ball(space, pos):
|
||||
ball = BaseBilliard(0, Category.BLACK_BALL, BLACK, pos)
|
||||
body = ball.rigid_body
|
||||
shape = ball.shape
|
||||
space.add(body, shape)
|
||||
balls.append(ball)
|
||||
return shape
|
||||
|
||||
|
||||
table = Table()
|
||||
balls: list[BaseBilliard] = [BaseBilliard(0, Category.CUE_BALL, WHITE, (200, 150))]
|
||||
cue = Cue(balls[0])
|
||||
|
||||
pygame.init()
|
||||
window = pygame.display.set_mode(SCREEN_SIZE)
|
||||
pygame.display.set_caption(TITLE)
|
||||
clock = pygame.time.Clock()
|
||||
space = pymunk.Space()
|
||||
space.gravity = (0, 0)
|
||||
|
||||
for _ in range(6):
|
||||
space.add(table.cushing_body[_], table.cushing_shape[_])
|
||||
space.add(balls[0].rigid_body, balls[0].shape)
|
||||
|
||||
force = 5
|
||||
add_force = False
|
||||
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
exit()
|
||||
if event.type == pygame.MOUSEMOTION:
|
||||
cue.mouse_pos = event.pos
|
||||
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||
if event.button == 1:
|
||||
add_force = True
|
||||
if event.type == pygame.MOUSEBUTTONUP:
|
||||
if event.button == 1:
|
||||
pos_x = event.pos[0] - (SCREEN_WIDTH - TABLE_WIDTH) / 2
|
||||
pos_y = event.pos[1] - (SCREEN_HEIGHT - TABLE_HEIGHT) / 2
|
||||
balls[0].rigid_body.velocity = pymunk.Vec2d(pos_x - balls[0].rigid_body.position.x,
|
||||
pos_y - balls[0].rigid_body.position.y).scale_to_length(force)
|
||||
add_force = False
|
||||
force = 5
|
||||
if event.button == 3:
|
||||
pos_x = event.pos[0] - (SCREEN_WIDTH - TABLE_WIDTH) / 2
|
||||
pos_y = event.pos[1] - (SCREEN_HEIGHT - TABLE_HEIGHT) / 2
|
||||
new_ball(space, (pos_x, pos_y))
|
||||
# balls[0].rigid_body.velocity = (256, 0)
|
||||
if add_force:
|
||||
force += 5
|
||||
if force >= 512:
|
||||
force = 512
|
||||
window.fill(WHITE)
|
||||
table.goal(space, balls)
|
||||
table.draw(window)
|
||||
cue.draw(window)
|
||||
for _ in balls:
|
||||
_.resistance()
|
||||
_.draw(window)
|
||||
space.step(1 / FPS)
|
||||
pygame.display.update()
|
||||
clock.tick(FPS)
|
||||
Loading…
Reference in new issue