change a lot

This commit is contained in:
Jeffrey Hsu 2025-02-01 03:52:39 +08:00
parent 6670715bf2
commit 3ef79886ec
4 changed files with 125 additions and 42 deletions

30
main.py
View File

@ -1,6 +1,6 @@
import pygame import pygame
from module import WIDTH, HEIGHT, BLOCK_SIZE, PLAYER_SPEED, GRAVITY, COLORS from module import WIDTH, HEIGHT, BLOCK_SIZE, PLAYER_SPEED, GRAVITY, COLORS, OPTIONS
from module.block import GrassBlock, StoneBlock, SandBlock, WaterBlock from module.block import GrassBlock, StoneBlock, SandBlock, WaterBlock
from module.player import Player from module.player import Player
from module.utils import generate_terrain from module.utils import generate_terrain
@ -31,6 +31,7 @@ while running:
player.put_block(bx, by) player.put_block(bx, by)
elif event.button == 3: # 右键拆除方块 elif event.button == 3: # 右键拆除方块
player.put_block(bx, by, 'delete') player.put_block(bx, by, 'delete')
player.on_ground = False # 不完美解决方法,挖方块时更新状态
elif event.type == pygame.KEYDOWN: elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_1: if event.key == pygame.K_1:
player.select_block(GrassBlock) player.select_block(GrassBlock)
@ -49,20 +50,16 @@ while running:
player.move(PLAYER_SPEED, 0) player.move(PLAYER_SPEED, 0)
if keys[pygame.K_w]: if keys[pygame.K_w]:
player.jump() player.jump()
if keys[pygame.K_F3]:
OPTIONS["debug"] = True
if keys[pygame.K_F4]:
OPTIONS["debug"] = False
# 物理模拟 # 物理模拟
player.velocity += GRAVITY if not player.on_ground:
player.velocity = GRAVITY
player.move(0, player.velocity) player.move(0, player.velocity)
# 碰撞检测
px = int(player.x // BLOCK_SIZE)
py = int(player.y // BLOCK_SIZE)
if py < len(world[px]) - 1 and world[px][py + 1] != "air":
player.on_ground = True
player.velocity = 0
else:
player.on_ground = False
# 绘制世界 # 绘制世界
screen.fill(COLORS["air"]) screen.fill(COLORS["air"])
for row in world: for row in world:
@ -74,13 +71,22 @@ while running:
pygame.draw.rect(screen, block.color, rect) pygame.draw.rect(screen, block.color, rect)
# 绘制玩家 # 绘制玩家
pygame.draw.circle(screen, COLORS["player"], (player.x, player.y), 10) player.draw(screen)
# 显示提示文字 # 显示提示文字
font = pygame.font.SysFont('KaiTi', 24) font = pygame.font.SysFont('KaiTi', 24)
text = font.render(f"Selected: {player.selected_block.id} (1-4切换方块)", True, (255, 255, 255)) text = font.render(f"Selected: {player.selected_block.id} (1-4切换方块)", True, (255, 255, 255))
screen.blit(text, (10, 10)) screen.blit(text, (10, 10))
# Debug Mode
if OPTIONS["debug"]:
debug_text = font.render("调试模式", True, (255, 255, 255))
screen.blit(debug_text, (10, 34))
for row in world:
for block in row:
if block.check_collision(player) and block.id != "air":
pygame.draw.circle(screen, COLORS["player"], block.get_center_vector(), 2)
pygame.display.flip() pygame.display.flip()
clock.tick(60) clock.tick(60)

View File

@ -13,6 +13,11 @@ COLORS = {
# 游戏参数 # 游戏参数
BLOCK_SIZE = 20 BLOCK_SIZE = 20
GRAVITY = 0.5 GRAVITY = 3
PLAYER_SPEED = 5 PLAYER_SPEED = 5
JUMP_FORCE = -12 JUMP_FORCE = -36
PLAYER_SIZE = 20
OPTIONS = {
"debug": False
}

View File

@ -1,4 +1,10 @@
from module import COLORS from module import COLORS, BLOCK_SIZE, PLAYER_SIZE
from typing import TYPE_CHECKING
# 解决循环导入
if TYPE_CHECKING:
from module.player import Player
class Block: class Block:
id = '' id = ''
@ -11,11 +17,45 @@ class Block:
self.x = x self.x = x
self.y = y self.y = y
def get_screen_x(self): def get_screen_x(self) -> int:
return self.x * 20 return self.x * BLOCK_SIZE
def get_screen_y(self): def get_screen_y(self) -> int:
return self.y * 20 return self.y * BLOCK_SIZE
def check_collision(self, player: 'Player') -> bool:
# 将玩家的碰撞体积看作为一个正方形,其形心坐标为 (player.x, player.y)
# 方块的坐标,即其的 (x, y) 是在 world 中的下标,需要将其转换为坐标
# 由于 下标*边长 计算得到的是这个方块左上角的坐标,所以要添加偏移量
# 计算得到形心坐标,偏移量是方块大小的一半
real_x = self.x * BLOCK_SIZE + BLOCK_SIZE / 2
real_y = self.y * BLOCK_SIZE + BLOCK_SIZE / 2
# 判断其是否碰撞仅需看其两形心间的距离,如果玩家的形心落在了方块碰撞
# 区域内,则发生碰撞,否则不发生碰撞。方块碰撞检测区域位于同形心坐标
# 边长为 PLAYER_SIZE + BLOCK_SIZE 的矩形区域
# 定界:
left = real_x - (PLAYER_SIZE + BLOCK_SIZE) / 2
right = real_x + (PLAYER_SIZE + BLOCK_SIZE) / 2
top = real_y - (PLAYER_SIZE + BLOCK_SIZE) / 2
bottom = real_y + (PLAYER_SIZE + BLOCK_SIZE) / 2
# 判碰撞:
return left < player.x < right and top < player.y < bottom
def get_collision_surface(self) -> tuple[int]:
# 获得方块表面界限(碰撞面)
# 上、右、下、左
real_x = self.x * BLOCK_SIZE + BLOCK_SIZE / 2
real_y = self.y * BLOCK_SIZE + BLOCK_SIZE / 2
top = real_y - BLOCK_SIZE / 2
bottom = real_y + BLOCK_SIZE / 2
left = real_x - BLOCK_SIZE / 2
right = real_x + BLOCK_SIZE / 2
return top, right, bottom, left
def get_center_vector(self):
return self.x * BLOCK_SIZE + BLOCK_SIZE / 2, self.y * BLOCK_SIZE + BLOCK_SIZE / 2
class GrassBlock(Block): class GrassBlock(Block):

View File

@ -1,6 +1,8 @@
from typing import Type, Literal from typing import Type, Literal
from module import WIDTH, HEIGHT, JUMP_FORCE import pygame
from module import WIDTH, HEIGHT, JUMP_FORCE, COLORS, PLAYER_SIZE, OPTIONS, BLOCK_SIZE
from module.block import AirBlock, Block from module.block import AirBlock, Block
@ -14,28 +16,47 @@ class Player:
self.selected_block = AirBlock self.selected_block = AirBlock
self.world = world self.world = world
def move(self, dx, dy): def move(self, dx: float, dy: float):
world_position = {'x': int(self.x) // 20, 'y': int(self.y) // 20} # 仅需检测玩家当前区块的上下左右的方块是否有碰撞体积
block_x_idx = int(self.x) // BLOCK_SIZE
block_y_idx = int(self.y) // BLOCK_SIZE
# TODONeed to judgment x and y in order not to raise Index Out Of Range Error top_block = self.world[block_x_idx][block_y_idx - 1] if block_y_idx > 0 else None
top_block = self.world[world_position['x']][world_position['y'] - 1] bottom_block = self.world[block_x_idx][block_y_idx + 1] if block_y_idx < HEIGHT // 20 - 1 else None
button_block = self.world[world_position['x']][world_position['y'] + 1] left_block = self.world[block_x_idx - 1][block_y_idx] if block_x_idx > 0 else None
left_block = self.world[world_position['x'] - 1][world_position['y']] right_block = self.world[block_x_idx + 1][block_y_idx] if block_x_idx < WIDTH // 20 - 1 else None
right_block = self.world[world_position['x'] + 1][world_position['y']]
if dx < 0 and left_block.collision: # 无碰撞体积时加 dx dy 有碰撞体积时,限制其值
new_x = left_block.get_screen_x() + 30 # 向下移动时且下面方块有碰撞体积,与上表面碰撞
elif dx > 0 and right_block.collision: if dy > 0 and bottom_block is not None and bottom_block.collision:
new_x = right_block.get_screen_x() - 10 if bottom_block.check_collision(self):
else: new_y = bottom_block.get_collision_surface()[0] - 10
new_x = self.x + dx self.on_ground = True
if dy < 0 and top_block.collision:
new_y = top_block.get_screen_y() + 30
elif dy > 0 and button_block.collision:
new_y = button_block.get_screen_y() - 10
else: else:
new_y = self.y + dy new_y = self.y + dy
# 向上移动时且上面方块有碰撞体积,与下表面碰撞
elif dy < 0 and top_block is not None and top_block.collision:
if top_block.check_collision(self):
new_y = top_block.get_collision_surface()[2] + 10
else:
new_y = self.y + dy
else:
new_y = self.y + dy
# 右移动
if dx > 0 and right_block is not None and right_block.collision:
if right_block.check_collision(self):
new_x = right_block.get_collision_surface()[3] - 10
else:
new_x = self.x + dx
# 左移动
elif dx < 0 and left_block is not None and left_block.collision:
if left_block.check_collision(self):
new_x = left_block.get_collision_surface()[1] + 10
else:
new_x = self.x + dx
else:
new_x = self.x + dx
if 0 <= new_x < WIDTH and 0 <= new_y < HEIGHT: if 0 <= new_x < WIDTH and 0 <= new_y < HEIGHT:
self.x = new_x self.x = new_x
@ -43,7 +64,7 @@ class Player:
def jump(self): def jump(self):
if self.on_ground: if self.on_ground:
self.velocity = JUMP_FORCE self.y += JUMP_FORCE
self.on_ground = False self.on_ground = False
def select_block(self, block: Type[Block]): def select_block(self, block: Type[Block]):
@ -56,3 +77,14 @@ class Player:
self.world[x][y] = AirBlock(x, y) self.world[x][y] = AirBlock(x, y)
else: else:
pass pass
def draw(self, screen: pygame.Surface):
pygame.draw.circle(screen, COLORS["player"], (self.x, self.y), PLAYER_SIZE // 2)
self.move(0, 0)
# 绘制玩家所在区块颜色
if OPTIONS['debug']:
block_x = int(self.x) // BLOCK_SIZE * BLOCK_SIZE
block_y = int(self.y) // BLOCK_SIZE * BLOCK_SIZE
rect = pygame.Rect(block_x, block_y, BLOCK_SIZE, BLOCK_SIZE)
pygame.draw.rect(screen, '#ffd5004d', rect)