import random
import math
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Line
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
# 游戏常量
GROUND_HEIGHT = 50
DINO_WIDTH = 40
DINO_HEIGHT = 60
OBSTACLE_WIDTH = 30
OBSTACLE_HEIGHT = [40, 60, 45] # 不同障碍物的高度
GRAVITY = 0.5
JUMP_FORCE = -12
GAME_SPEED = 5
CLOUD_SPEED = 1.5
class DinoGame(Widget):
def __init__(self, **kwargs):
super(DinoGame, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_keyboard_down)
# 游戏状态
self.score = 0
self.high_score = 0
self.game_over = False
self.game_started = False
# 恐龙参数
self.dino_x = 50
self.dino_y = GROUND_HEIGHT
self.dino_jump_velocity = 0
self.is_jumping = False
self.dino_frame = 0 # 用于动画切换
# 障碍物
self.obstacles = []
self.obstacle_speed = GAME_SPEED
self.obstacle_timer = 0
self.obstacle_interval = 100 # 障碍物生成间隔
# 云朵
self.clouds = []
self.init_clouds()
# 地面
self.ground_offset = 0
# 分数标签
self.score_label = Label(text='Score: 0', font_size='20sp', pos=(20, Window.height - 40))
self.add_widget(self.score_label)
# 游戏结束标签
self.game_over_label = Label(text='GAME OVER', font_size='40sp',
pos=(Window.width / 2 - 100, Window.height / 2), opacity=0)
self.add_widget(self.game_over_label)
# 开始提示
self.start_label = Label(text='Press SPACE to start', font_size='20sp',
pos=(Window.width / 2 - 100, Window.height / 2))
self.add_widget(self.start_label)
# 重新开始按钮
self.restart_btn = Button(text='Restart', size_hint=(None, None), size=(100, 50),
pos=(Window.width / 2 - 50, Window.height / 2 - 100), opacity=0)
self.restart_btn.bind(on_press=self.restart_game)
self.add_widget(self.restart_btn)
# 绑定更新函数
Clock.schedule_interval(self.update, 1.0 / 60.0)
def init_clouds(self):
for i in range(3):
self.clouds.append({
'x': random.randint(0, Window.width),
'y': random.randint(Window.height / 2, Window.height - 50),
'speed': random.uniform(0.5, 1.5) * CLOUD_SPEED,
'size': random.randint(50, 100)
})
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers, **kwargs):
"""修正后的键盘事件处理"""
if keycode[1] == 'spacebar':
if not self.game_started and not self.game_over:
self.game_started = True
self.start_label.opacity = 0
elif not self.is_jumping and not self.game_over:
self.dino_jump_velocity = JUMP_FORCE
self.is_jumping = True
elif self.game_over:
self.restart_game()
return True
def jump(self):
if not self.is_jumping and not self.game_over and self.game_started:
self.dino_jump_velocity = JUMP_FORCE
self.is_jumping = True
def update_clouds(self):
for cloud in self.clouds:
cloud['x'] -= cloud['speed']
if cloud['x'] < -cloud['size']:
cloud['x'] = Window.width
cloud['y'] = random.randint(Window.height / 2, Window.height - 50)
cloud['speed'] = random.uniform(0.5, 1.5) * CLOUD_SPEED
def update_obstacles(self):
# 生成新障碍物
self.obstacle_timer += 1
if self.obstacle_timer >= self.obstacle_interval and self.game_started and not self.game_over:
self.obstacle_timer = 0
obstacle_type = random.randint(0, 2)
self.obstacles.append({
'x': Window.width,
'type': obstacle_type,
'passed': False
})
# 随机间隔
self.obstacle_interval = random.randint(70, 150)
# 移动障碍物
for obstacle in self.obstacles[:]:
obstacle['x'] -= self.obstacle_speed
if obstacle['x'] < -OBSTACLE_WIDTH:
self.obstacles.remove(obstacle)
def update_score(self):
for obstacle in self.obstacles:
if not obstacle['passed'] and obstacle['x'] + OBSTACLE_WIDTH < self.dino_x:
obstacle['passed'] = True
self.score += 1
self.score_label.text = f'Score: {self.score}'
# 随着分数增加难度
if self.score % 10 == 0:
self.obstacle_speed += 0.2
def check_collision(self):
dino_rect = {
'x': self.dino_x,
'y': self.dino_y,
'width': DINO_WIDTH,
'height': DINO_HEIGHT
}
for obstacle in self.obstacles:
obstacle_rect = {
'x': obstacle['x'],
'y': GROUND_HEIGHT,
'width': OBSTACLE_WIDTH,
'height': OBSTACLE_HEIGHT[obstacle['type']]
}
if (dino_rect['x'] < obstacle_rect['x'] + obstacle_rect['width'] and
dino_rect['x'] + dino_rect['width'] > obstacle_rect['x'] and
dino_rect['y'] < obstacle_rect['y'] + obstacle_rect['height'] and
dino_rect['y'] + dino_rect['height'] > obstacle_rect['y']):
self.game_over = True
self.game_over_label.opacity = 1
self.restart_btn.opacity = 1
if self.score > self.high_score:
self.high_score = self.score
def update_dino(self):
# 跳跃物理
if self.is_jumping:
self.dino_y -= self.dino_jump_velocity
self.dino_jump_velocity += GRAVITY
if self.dino_y <= GROUND_HEIGHT:
self.dino_y = GROUND_HEIGHT
self.is_jumping = False
# 奔跑动画
if not self.is_jumping and self.game_started and not self.game_over:
self.dino_frame = (self.dino_frame + 1) % 10
def restart_game(self, *args):
self.score = 0
self.score_label.text = 'Score: 0'
self.obstacles = []
self.obstacle_speed = GAME_SPEED
self.dino_y = GROUND_HEIGHT
self.is_jumping = False
self.game_over = False
self.game_started = True
self.game_over_label.opacity = 0
self.restart_btn.opacity = 0
def update(self, dt):
if not self.game_started or self.game_over:
return
# 更新地面滚动
self.ground_offset = (self.ground_offset + self.obstacle_speed) % Window.width
self.update_dino()
self.update_clouds()
self.update_obstacles()
self.update_score()
self.check_collision()
self.draw()
def draw(self):
self.canvas.clear()
# 绘制背景
with self.canvas:
Color(0.9, 0.9, 0.9) # 浅灰色背景
Rectangle(pos=(0, 0), size=(Window.width, Window.height))
# 绘制云朵
Color(0.8, 0.8, 0.8)
for cloud in self.clouds:
Rectangle(pos=(cloud['x'], cloud['y']),
size=(cloud['size'], cloud['size'] / 2))
# 绘制地面
Color(0.4, 0.4, 0.4)
Rectangle(pos=(0, 0), size=(Window.width, GROUND_HEIGHT))
# 地面装饰线
Color(0.3, 0.3, 0.3)
Line(points=[0, GROUND_HEIGHT, Window.width, GROUND_HEIGHT], width=1)
# 绘制障碍物
Color(0.2, 0.6, 0.2) # 绿色障碍物
for obstacle in self.obstacles:
height = OBSTACLE_HEIGHT[obstacle['type']]
Rectangle(pos=(obstacle['x'], GROUND_HEIGHT),
size=(OBSTACLE_WIDTH, height))
# 绘制恐龙
Color(0.2, 0.2, 0.2) # 黑色恐龙
# 简单的恐龙形状 - 可以根据需要替换为更复杂的图形
# 身体
Rectangle(pos=(self.dino_x, self.dino_y),
size=(DINO_WIDTH, DINO_HEIGHT))
# 头
head_height = DINO_HEIGHT * 0.6
Rectangle(pos=(self.dino_x + DINO_WIDTH, self.dino_y + DINO_HEIGHT - head_height),
size=(DINO_WIDTH * 0.5, head_height))
# 腿 - 根据动画帧切换
leg_width = DINO_WIDTH * 0.2
leg_height = DINO_HEIGHT * 0.3
if self.dino_frame < 5 or self.is_jumping:
# 前腿向后
Rectangle(pos=(self.dino_x + DINO_WIDTH * 0.2, self.dino_y - leg_height),
size=(leg_width, leg_height))
# 后腿向前
Rectangle(pos=(self.dino_x + DINO_WIDTH * 0.6, self.dino_y - leg_height),
size=(leg_width, leg_height))
else:
# 前腿向前
Rectangle(pos=(self.dino_x + DINO_WIDTH * 0.2, self.dino_y - leg_height),
size=(leg_width, leg_height))
# 后腿向后
Rectangle(pos=(self.dino_x + DINO_WIDTH * 0.6, self.dino_y - leg_height),
size=(leg_width, leg_height))
class DinoApp(App):
def build(self):
game = DinoGame()
Window.bind(on_key_down=game._on_keyboard_down)
return game
if __name__ == '__main__':
DinoApp().run()
C:\Users\V82406169\PycharmProjects\ikun\.venv\Scripts\python.exe C:\Users\V82406169\PycharmProjects\ikun\.venv\dino.py
[INFO ] [Logger ] Record log in C:\Users\V82406169\.kivy\logs\kivy_25-12-26_41.txt
[INFO ] [deps ] Successfully imported "kivy_deps.angle" 0.4.0
[INFO ] [deps ] Successfully imported "kivy_deps.glew" 0.3.1
[INFO ] [deps ] Successfully imported "kivy_deps.sdl2" 0.7.0
[INFO ] [Kivy ] v2.3.0
[INFO ] [Kivy ] Installed at "C:\Users\V82406169\PycharmProjects\ikun\.venv\lib\site-packages\kivy\__init__.py"
[INFO ] [Python ] v3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:01:55) [MSC v.1900 32 bit (Intel)]
[INFO ] [Python ] Interpreter at "C:\Users\V82406169\PycharmProjects\ikun\.venv\Scripts\python.exe"
[INFO ] [Logger ] Purge log fired. Processing...
[INFO ] [Logger ] Purge finished!
[INFO ] [Factory ] 195 symbols loaded
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2 (img_pil, img_ffpyplayer ignored)
[INFO ] [Window ] Provider: sdl2
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] GLEW initialization succeeded
[INFO ] [GL ] Backend used <glew>
[INFO ] [GL ] OpenGL version <b'3.3 (Compatibility Profile) Mesa 21.2.0-devel (git-ac6211dc0e)'>
[INFO ] [GL ] OpenGL vendor <b'VMware, Inc.'>
[INFO ] [GL ] OpenGL renderer <b'SVGA3D; build: RELEASE; LLVM;'>
[INFO ] [GL ] OpenGL parsed version: 3, 3
[INFO ] [GL ] Shading version <b'3.30'>
[INFO ] [GL ] Texture max size <8192>
[INFO ] [GL ] Texture max units <16>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [Text ] Provider: sdl2
[INFO ] [Base ] Start application main loop
[INFO ] [GL ] NPOT texture support is available
[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "C:\Users\V82406169\PycharmProjects\ikun\.venv\dino.py", line 282, in <module>
DinoApp().run()
File "C:\Users\V82406169\PycharmProjects\ikun\.venv\lib\site-packages\kivy\app.py", line 956, in run
runTouchApp()
File "C:\Users\V82406169\PycharmProjects\ikun\.venv\lib\site-packages\kivy\base.py", line 574, in runTouchApp
EventLoop.mainloop()
File "C:\Users\V82406169\PycharmProjects\ikun\.venv\lib\site-packages\kivy\base.py", line 341, in mainloop
self.window.mainloop()
File "C:\Users\V82406169\PycharmProjects\ikun\.venv\lib\site-packages\kivy\core\window\window_sdl2.py", line 778, in mainloop
self.modifiers):
File "kivy\\_event.pyx", line 727, in kivy._event.EventDispatcher.dispatch
File "kivy\\_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
File "kivy\\_event.pyx", line 1231, in kivy._event.EventObservers._dispatch
TypeError: _on_keyboard_down() takes 5 positional arguments but 6 were given
最新发布