forked from MykleR/Pygame-DoodleJump
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayer.py
165 lines (138 loc) · 5.94 KB
/
player.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
"""
CopyLeft 2021 Michael Rouves
This file is part of Pygame-DoodleJump.
Pygame-DoodleJump is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Pygame-DoodleJump is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Pygame-DoodleJump. If not, see <https://www.gnu.org/licenses/>.
"""
from math import copysign
import pygame
from pygame.event import Event
from pygame.locals import KEYDOWN, KEYUP, K_LEFT, K_RIGHT
from pygame.math import Vector2
from pygame.sprite import collide_rect
import settings as config
from level import Level
from singleton import Singleton
from sprite import Sprite
# Return the sign of a number: getsign(-5)-> -1
def getsign(x):
return copysign(1, x)
class Player(Sprite, Singleton):
"""
A class to represent the player.
Manages player's input,physics (movement...).
Can be access via Singleton: Player.instance.
(Check Singleton design pattern for more info).
"""
# (Overriding Sprite.__init__ constructor)
def __init__(self, game, *args):
# calling default Sprite constructor
Sprite.__init__(self, *args)
self.game = game # ref to game
self.__startrect = self.rect.copy()
self.__maxvelocity = Vector2(config.PLAYER_MAX_SPEED, 100)
self.__startspeed = 1.5
self._velocity = Vector2()
self._input = 0
self._jumpforce = config.PLAYER_JUMPFORCE
self._bonus_jumpforce = config.PLAYER_BONUS_JUMPFORCE
self.gravity = config.GRAVITY
self.accel = .5
self.deccel = .6
self.dead = False
# Load player image
self._image = pygame.image.load('images/DOG.png').convert_alpha()
self.rect = self._image.get_rect(topleft=(self.rect.x, self.rect.y))
self.camera_rect = self.rect.copy()
# Load sound
self.jump_sound = pygame.mixer.Sound('sounds/jump.ogg')
def _fix_velocity(self) -> None:
""" Set player's velocity between max/min.
Should be called in Player.update().
"""
self._velocity.y = min(self._velocity.y, self.__maxvelocity.y)
self._velocity.y = round(max(self._velocity.y, -self.__maxvelocity.y), 2)
self._velocity.x = min(self._velocity.x, self.__maxvelocity.x)
self._velocity.x = round(max(self._velocity.x, -self.__maxvelocity.x), 2)
def reset(self) -> None:
""" Called only when game restarts (after player death)."""
self._velocity = Vector2()
self.rect = self.__startrect.copy()
self.camera_rect = self.__startrect.copy()
self.dead = False
def handle_event(self, event: Event) -> None:
""" Called in main loop foreach user input event.
:param event: user input event
"""
# Check if start moving
if event.type == KEYDOWN:
# Moves player only on x-axis (left/right)
if event.key == K_LEFT:
self._velocity.x = -self.__startspeed
self._input = -1
elif event.key == K_RIGHT:
self._velocity.x = self.__startspeed
self._input = 1
# Check if stop moving
elif event.type == KEYUP:
if (event.key == K_LEFT and self._input == -1) or (
event.key == K_RIGHT and self._input == 1):
self._input = 0
def jump(self, force: float = None) -> None:
if not force: force = self._jumpforce
self._velocity.y = -force
if self.game.sound_on:
self.jump_sound.play()
def onCollide(self, obj: Sprite) -> None:
self.rect.bottom = obj.rect.top
self.jump()
def collisions(self, game_instance) -> None:
""" Checks for collisions with level.
Should be called in Player.update().
"""
lvl = Level.instance
if not lvl:
return
for platform in lvl.platforms:
# check falling and colliding <=> isGrounded ?
if self._velocity.y > .5:
# check collisions with platform's spring bonus
if platform.bonus and collide_rect(self, platform.bonus):
self.onCollide(platform.bonus)
self.jump(platform.bonus.force)
# check collisions with platform
if collide_rect(self, platform):
self.onCollide(platform)
platform.onCollide()
# Check red squares collisions
for square in game_instance.squares:
if square['spawned'] and self.rect.colliderect(square['rect']):
self.dead = True
def update(self, game_instance) -> None:
""" For position and velocity updates.
Should be called each frame.
"""
# Check if player out of screen: should be dead
if self.camera_rect.y > config.YWIN * 2:
self.dead = True
return
# Velocity update (apply gravity, input acceleration)
self._velocity.y += self.gravity
if self._input: # accelerate
self._velocity.x += self._input * self.accel
elif self._velocity.x: # deccelerate
self._velocity.x -= getsign(self._velocity.x) * self.deccel
self._velocity.x = round(self._velocity.x)
self._fix_velocity()
# Position Update (prevent x-axis to be out of screen)
self.rect.x = (self.rect.x + self._velocity.x) % (config.XWIN - self.rect.width)
self.rect.y += self._velocity.y
self.collisions(game_instance)