零、序章
这是一个使用Python写的过马路游戏。
可以练习一些面向对象的知识。
由于全写注释CSND说我字太少,就统一写代码外面来
实现的功能有:
- 汽车的随机刷新、汽车的整体移动、碰撞检测
- 玩家的移动(只能向上移动。。。没有更多操作了)
- 关卡显示以及根据关卡自动调整汽车移动速度
可以改进的地方有:
- 暂停功能
- 碰撞检测可以优化得更精准
- 感觉有卡顿,不知道是单次循环的停顿时间根据level变化造成的错觉,还是运行到后面车太多了造成了实际的卡顿
一、游戏主程序
- cal_limits函数:根据screen计算边界
- quit_game函数:控制退出游戏
- 实例化屏幕对象、玩家对象、汽车管理员对象、计分板对象
- 每次循环停顿时间根据level决定,level越高,停顿时间越短,达到汽车运动越快的效果(也可以通过更改汽车单次移动常量来加速)
- 每次循环汽车要刷新、移动、进行碰撞检测
import time
from turtle import Screen
from player import Player
from car_manager import CarManager
from scoreboard import ScoreBoard
screen = Screen()
screen.setup(width=600, height=600)
screen.tracer(0)
def cal_limits(screen):
u_limit = screen.window_height() / 2
d_limit = -screen.window_height() / 2
l_limit = -screen.window_width() / 2
r_limit = screen.window_width() / 2
return u_limit, d_limit, l_limit, r_limit
def quit_game():
global game_is_on
game_is_on = False
limits = cal_limits(screen)
player = Player()
car_manager = CarManager(limits=limits)
screen.listen()
screen.onkey(key='Up', fun=player.move)
screen.onkey(key='q', fun=quit_game)
scoreboard = ScoreBoard(limits)
game_is_on = True
while game_is_on:
time.sleep(0.11 - scoreboard.level * 0.015)
screen.update()
car_manager.add_car((limits[0] - 50, limits[1] + 100, limits[3], limits[3] + 300))
car_manager.move(limits)
if car_manager.collision_check(player):
scoreboard.game_clear('Game Over!!!')
player.respawn()
scoreboard.refresh_level()
if player.check_finish():
scoreboard.refresh_level()
二、玩家类
- 具有初始位置、单次移动距离、终点线等常量
- 继承自Turtle类
- 每次抵达终点线使用respawn函数回到起点
- 使用check_finish函数检测是否抵达终点线
STARTING_POSITION = (0, -280)
MOVE_DISTANCE = 10
FINISH_LINE_Y = 280
from turtle import Turtle
class Player(Turtle):
def __init__(self):
super().__init__()
self.penup()
self.shape('turtle')
self.goto(STARTING_POSITION)
self.setheading(90)
def move(self):
self.forward(MOVE_DISTANCE)
def respawn(self):
self.goto(STARTING_POSITION)
def check_finish(self):
if self.pos()[1] >= FINISH_LINE_Y:
self.respawn()
return True
三、汽车管理员类
- 具有颜色、刷新距离、单次移动距离、刷新率等常量
- 本身是个列表,列表内放Turtle类
- 一开始在给定范围(基本是整个屏幕内)刷新,之后就都在屏幕右边的范围刷新
- add_car函数:根据刷新率在屏幕右侧随机刷新汽车,,每次都要遍历汽车检测刷新距离是否满足STARTING_MOVE_DISTANCE常量,满足就加入列表
- move函数:遍历列表,每个车都向左移动
- collision_check碰撞检测函数:有点难做,因为我不想简单地使用distance,感觉这个是不是不能用在方形物体的碰撞检测上?我的总体思路是将整个屏幕看作一个网格,每个格子边长为10,计算出汽车上下左右边界和玩家的上下边界(左右边界感觉不用算也能判断得很精准)在哪个格子后再进行碰撞检测。
COLORS = ["red", "orange", "yellow", "green", "blue", "purple"]
STARTING_MOVE_DISTANCE = 10
MOVE_INCREMENT = 10
REFRESH_RATE = 0.3
import random
from turtle import Turtle
class CarManager:
def __init__(self, limits):
self.cars = []
spawn_range = (limits[0] - 20, limits[1] + 100, limits[2], limits[3] + 300)
for i in range(25):
self.add_car(spawn_range)
def add_car(self, spawn_range):
if random.random() < REFRESH_RATE:
new_pos = (random.randint(spawn_range[2], spawn_range[3]) // 20) * 20, \
(random.randint(spawn_range[1], spawn_range[0]) // 20) * 20
while self.cars:
for old_car in self.cars:
if old_car.distance(new_pos) <= STARTING_MOVE_DISTANCE:
new_pos = (random.randint(spawn_range[2], spawn_range[3]) // 20) * 20, \
(random.randint(spawn_range[1], spawn_range[0]) // 20) * 20
break
else:
break
new_car = Turtle(shape='square')
new_car.shapesize(stretch_wid=1, stretch_len=2)
new_car.penup()
new_car.color(random.choice(COLORS))
new_car.setheading(180)
new_car.goto(new_pos)
self.cars.append(new_car)
def move(self, limits):
for car in self.cars:
car.forward(MOVE_INCREMENT)
if car.pos()[0] < limits[2] - 20:
car.ht()
self.cars.remove(car)
def collision_check(self, player):
player_up_y_block = (player.pos()[1] + 10) // 10
player_down_y_block = (player.pos()[1] - 10) // 10
for car in self.cars:
car_left_x_block = (car.pos()[0] - 20) // 10
car_right_x_block = (car.pos()[0] + 20) // 10
car_up_y_block = (car.pos()[1] + 10) // 10
car_down_y_block = (car.pos()[1] - 10) // 10
if car_left_x_block <= 0 <= car_right_x_block and \
(car_down_y_block < player_up_y_block < car_up_y_block or
car_down_y_block < player_down_y_block < car_up_y_block):
return True
四、计分板类
- 具有字体、最大关数等常量
- 计分板类继承自Turtle类,具有额外的level属性
- 其他功能和之前发的贪吃蛇、乒乓类似,就不赘述了,只多了个检测通关的功能
FONT = ("Courier", 24, "normal")
MAX_LEVEL = 7
import time
from turtle import Turtle
class ScoreBoard(Turtle):
def __init__(self, limits):
super().__init__()
self.level = 1
self.penup()
self.ht()
self.goto(limits[2] + 20, limits[0] - 50)
self.write(f"Level:{self.level}", font=FONT)
def refresh_level(self):
if self.level >= MAX_LEVEL:
self.game_clear('Game Clear!!!')
self.clear()
self.level += 1
self.write(f"Level:{self.level}", font=FONT)
def game_clear(self, info):
self.level = 0
pos = self.pos()
self.goto(-100, 0)
self.write(info, font=FONT)
self.goto(pos)
time.sleep(5)