Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Scene, Game Collision & Gravity Issues
-
UPDATE:
So, I managed to create a function (based on this) which indicates which edge of the wall is hit called
HitTest
.Now, in the first option (current state) the problem is that I can't jump anymore.
The other option: when I change in thePlayer
class thejump
method fromif touch.location in buttons[2].bbox and self.onGround
toif touch.location in buttons[2].bbox
, which basically means I don't have to be on the ground to jump, gravity & jump is working fine. The only problem now is as soon as I move and jump I can glitch through the walls.Both option aren't optimal but the last one is definitely closer to my aim.
Any tipps? Or did I miss something crucial?
Here is the code:
import scene from scene import * import sound import random import math import level import ui A = Action # Constants PI = math.pi #Screen Size SCREEN_W = get_screen_size().w SCREEN_H = get_screen_size().h # Amount of frames till you can shoot again WAIT_TIME = 30 BULLET_SPEED = 10 ROTATION_LEFT = PI/2 ROTATION_RIGHT = 3*PI/2 # Gravity GRAVITY = 4 def HitTest(entity, object, output = 0): ''' max_x = right edge, min_x = left edge, max_y = top edge, min_y = bottom edge ''' rect_1 = entity.bbox rect_2 = object.bbox if rect_1.max_x >= rect_2.x and rect_1.x <= rect_2.max_x: if rect_1.max_y >= rect_2.y and rect_1.y <= rect_2.max_y: output = 1 if output: origin_1 = rect_1.center() origin_2 = rect_2.center() dx = origin_1.x - origin_2.x dy = origin_1.y - origin_2.y if dx <= 0: #print("Hit from Right") output_1 = 1 if dx >= 0: #print("Hit from Left") output_1 = 2 if dy <= 0: #print("Hit from Top") output_2 = 1 if dy >= 0: #print("Hit from Bottom") output_2 = 2 if abs(dx) > abs(dy): #print("Hit Right or Left") output = output_1 if abs(dx) < abs(dy): #print("Hit Top or Bottom") output = output_2 + 2 return output class Buttons(SpriteNode): ''' Buttons Class ''' def __init__(self, name, appearance, position, scale, alpha, buttons): SpriteNode.__init__(self, appearance) self.name = name self.position = position self.z_position = 3 self.scale = scale self.alpha = alpha buttons.append(self) def __str__(self): return self.name class Buttons_Text(LabelNode): def __init__(self, name, text, button, buttons_text): LabelNode.__init__(self, text, font=('Futura', 13), color='white') self.name = name self.position = (button.position.x, button.position.y - 50) self.z_position = 1 buttons_text.append(self) class Brick(ShapeNode): def __init__(self, name, position, z_position, brick_w, brick_h): path = ui.Path.rect(position[0], position[1], brick_w, brick_h) ShapeNode.__init__(self, path, '#b7b7b7', 'clear') self.name = name self.position = position self.z_position = z_position def __str__(self): return self.name class Map(ShapeNode): def __init__(self, walls): ''' Generates the map ''' lines = level.level.splitlines() brick_w = 40 brick_h = 40 col = 0 row = 0 for line in lines: for i in line: if i == "W": x = 17 + row * brick_w y = 800 - col * brick_h wall = Brick('Brick', (x, y), 0, brick_w, brick_h) walls.append(wall) row += 1 else: x = 17 + row * brick_w y = 800 - col * brick_h row += 1 col += 1 row = 0 class Entity(SpriteNode): ''' Generate an Entity ''' def __init__(self, appearance): SpriteNode.__init__(self, appearance) class Bullet(Entity): def __init__(self, name, appearance, x, y, rotation): super().__init__(appearance) self.position = (x, y) self.rotation = rotation def update(bullets_left, bullets_right): for bullet in list(bullets_left): new_x = bullet.position.x - BULLET_SPEED if new_x >= 0 and new_x <= SCREEN_W: bullet.position = (new_x, bullet.position.y) else: bullet.remove_from_parent() bullets_left.remove(bullet) for bullet in list(bullets_right): new_x = bullet.position.x + BULLET_SPEED if new_x >= 0 and new_x <= SCREEN_W: bullet.position = (new_x, bullet.position.y) else: bullet.remove_from_parent() bullets_right.remove(bullet) class Player(Entity): ''' Player Class ''' def __init__(self, name, appearance, position, z_position): super().__init__(appearance) self.name = name self.position = position self.z_position = z_position self.velocity = Vector2(0, 0) self.speed = 5 self.jump_strength = 100 self.onGround = False # Controls self.leftKey = False self.rightKey = False self.jumpKey = False self.shootKey = False def move_left(self, buttons, touches): for touch in touches.values(): if touch.location in buttons[0].bbox: self.leftKey = True self.velocity.x = -self.speed new_x = self.position.x + self.velocity.x if new_x >= 0 and new_x <= SCREEN_W: self.position = (new_x, self.position.y) def move_right(self, buttons, touches): for touch in touches.values(): if touch.location in buttons[1].bbox: self.rightKey = True self.velocity.x = self.speed new_x = self.position.x + self.velocity.x if new_x >= 0 and new_x <= SCREEN_W: self.position = (new_x, self.position.y) def jump(self, buttons, touches): for touch in touches.values(): if touch.location in buttons[2].bbox and self.onGround: self.jumpKey = True self.onGround = False self.velocity.y = self.jump_strength new_y = self.position.y + self.velocity.y action = A.move_to(self.position.x, new_y, 1, TIMING_LINEAR) if new_y <= SCREEN_H: self.run_action(action) def shoot(self, instance, player_direction, buttons, touches, bullets_left, bullets_right): for touch in touches.values(): if touch.location in buttons[3].bbox: self.shootKey = True if player_direction.position.x < self.position.x and instance.frame_counter >= WAIT_TIME: bullet = Bullet("Bullet Right", 'spc:LaserRed10', self.position.x - 20, self.position.y - 40, ROTATION_LEFT) bullets_left.append(bullet) instance.add_child(bullet) instance.frame_counter = 0 if player_direction.position.x > self.position.x and instance.frame_counter >= WAIT_TIME: bullet = Bullet("Bullet Left", 'spc:LaserRed10', self.position.x + 20, self.position.y - 40, ROTATION_RIGHT) bullets_right.append(bullet) instance.add_child(bullet) instance.frame_counter = 0 def check_touch(self, buttons, touches): if len(touches.values()) > 0: for touch in touches.values(): if touch.location not in buttons[0].bbox: self.leftKey = False if touch.location not in buttons[1].bbox: self.rightKey = False if not touch.location in buttons[2].bbox: self.jumpKey = False if not touch.location in buttons[3].bbox: self.shootKey = False else: self.leftKey = False self.rightKey = False self.jumpKey = False self.shootKey = False def check_velocity(self): #print(self.velocity) if not self.leftKey and not self.rightKey: self.velocity = Vector2(0, self.velocity.y) if not self.jumpKey and self.onGround: self.velocity = Vector2(self.velocity.x, 0) def collision_right(self, player_bbox, w): new_x = w.bbox.min_x - self.bbox.w / 2 self.position = (new_x, self.position.y) self.velocity.y = 0 #print("collide right") return def collision_left(self, player_bbox, w): new_x = w.bbox.max_x + self.bbox.w / 2 self.position = (new_x, self.position.y) self.velocity.y = 0 #print("collide left") return def collision_top(self, player_bbox, w): new_y = w.bbox.min_y - self.bbox.h / 2 self.position = (self.position.x, new_y) self.onGround = False #print("collide top", self.onGround) return def collision_bottom(self, player_bbox, w): new_y = w.bbox.max_y + self.bbox.h / 2 self.position = (self.position.x, new_y) self.onGround = True self.velocity.y = 0 #print("collide down", self.onGround) return def collision(self, player_bbox, walls): for w in walls: result = HitTest(self, w) '''0 : No Collision, 1 : Collision from Right, 2 : Collision from Left, 3 : Collision from Top, 4 : Collision from Bottom ''' if result == 0: self.onGround = False if result == 1: self.collision_right(player_bbox, w) if result == 2: self.collision_left(player_bbox, w) if result == 3: self.collision_top(player_bbox, w) if result == 4: self.collision_bottom(player_bbox, w) def gravity(self): ''' Describes the Gravity''' if not self.onGround: self.velocity.y = -GRAVITY new_y = self.position.y + self.velocity.y if new_y >= 0: self.position = (self.position.x, new_y) class Hitbox(ShapeNode): ''' Creates a visible hitbox for an entity based on the bbox of the entity. entity = Object, Player, Enemy etc. entity_objects = List of objects for that entity dw, dh = how much smaller the widht/height of the hitbox should be (based on the bbox of the entity) ''' def __init__(self, entity, entity_objects, dw, dh): self.dw = dw self.dh = dh self.width = entity.bbox.w - self.dw self.height = entity.bbox.h - self.dh path = ui.Path.rect(0, 0, self.width, self.height) ShapeNode.__init__(self, path) self.fill_color = 'white' self.stroke_color = 'clear' self.position = (entity.position.x, entity.position.y - self.dh/2) self.alpha = 0.1 entity_objects.append(self) def bbox(self, entity): box = Rect(self.position.x, self.position.y, self.width, self.height) return box def update(self, entity, rect): self.position = (entity.position.x, entity.position.y - self.dh/2) rect.x = self.position.x - entity.bbox.w / 2 rect.y = self.position.y - entity.bbox.h / 2 class Entity_Direction(ShapeNode): ''' Creates a object which indicates the direction of entity dh = for adjusting the height ''' def __init__(self, entity, entity_objects): path = ui.Path.rect(0, 0, entity.size.w, 20) ShapeNode.__init__(self, path) self.dh = 40 self.fill_color = 'white' self.stroke_color = 'clear' self.position = (entity.position.x + 20, entity.position.y - self.dh) self.alpha = 0.1 # Last position (True = right, False = left) self.last_direction = True entity_objects.append(self) def update(self, entity): if entity.velocity.x > 0: self.position = (entity.position.x + 20, entity.position.y - self.dh) self.last_direction = True if entity.velocity.x < 0: self.position = (entity.position.x - 20, entity.position.y - self.dh) self.last_direction = False if entity.velocity.x == 0: if self.last_direction: self.position = (entity.position.x + 20, entity.position.y - self.dh) if not self.last_direction: self.position = (entity.position.x - 20, entity.position.y - self.dh) class Game (Scene): def setup(self): # Game running self.game_running = False # Frame Counter to limit the amount of shot bullets self.frame_counter = 0 # Lists for handling different actions & other functions self.walls = [] self.buttons= [] self.buttons_text = [] self.player_objects = [] self.bullets_left = [] self.bullets_right = [] # Generate Map map = Map(self.walls) # Buttons (order is important) self.left_button = Buttons("Left Button", 'iow:arrow_left_a_256', (80, 80), 0.5, 0.25, self.buttons) self.right_button = Buttons("Right Button", 'iow:arrow_right_a_256', (200, 80), 0.5, 0.25, self.buttons) self.jump_button = Buttons("Jump Button", 'iow:nuclear_256', (1000, 80), 0.3, 0.25, self.buttons) self.shoot_button = Buttons("Shoot Button", 'iow:ios7_plus_256', (1100, 80), 0.3, 0.25, self.buttons) self.jump_text = Buttons_Text('Jump Text', 'Jump', self.jump_button, self.buttons_text) self.shoot_text = Buttons_Text('Shoot Text', 'Shoot', self.shoot_button, self.buttons_text) # Player Objects self.player = Player('Player', 'plf:AlienGreen_front', (100, 200), 1) self.player_visible_bbox = Hitbox(self.player, self.player_objects, 10, 50) self.player_bbox = self.player_visible_bbox.bbox(self.player) print(self.player_bbox) print(self.player_visible_bbox.position) print(self.player.position) self.player_direction = Entity_Direction(self.player, self.player_objects) # Add Childs self.add_child(self.player) for p in self.player_objects: self.add_child(p) for b in self.buttons: self.add_child(b) for b_t in self.buttons_text: self.add_child(b_t) for w in self.walls: self.add_child(w) def did_change_size(self): pass def update(self): if self.game_running == True: # Frame Counter, 60 frames == 1 sec self.frame_counter += 1 # Game Gravity self.player.gravity() # Player Objects self.player_direction.update(self.player) self.player_visible_bbox.update(self.player, self.player_bbox) # Controls self.player.move_left(self.buttons, self.touches) self.player.move_right(self.buttons, self.touches) self.player.jump(self.buttons, self.touches) self.player.shoot(self, self.player_direction, self.buttons, self.touches, self.bullets_left, self.bullets_right) self.player.check_touch(self.buttons, self.touches) self.player.check_velocity() # Bullets Bullet.update(self.bullets_left, self.bullets_right) # Game Collision self.player.collision(self.player_bbox, self.walls) def touch_began(self, touch): if touch.location > (0, 0): self.game_running = True def touch_moved(self, touch): pass def touch_ended(self, touch): pass if __name__ == '__main__': run(Game(), LANDSCAPE, show_fps=True)
-
Perhaps this, but to late to study it deeply, good luck
def collision(self, player_bbox, walls): for w in walls: result = HitTest(self, w) '''0 : No Collision, 1 : Collision from Right, 2 : Collision from Left, 3 : Collision from Top, 4 : Collision from Bottom ''' #if result == 0: # self.onGround = False
-
@xvid you may want to look for a good tutorial for platformer physics -- might go through all the important cases to think through.
https://gamedevelopment.tutsplus.com/series/basic-2d-platformer-physics--cms-998 is one, though I haven't read it
-
-
@xvid I think the problem comes from your collision method.
Even if you hit a wall, you continue your loop and if you don't intersect a next wall, you have lost your result.
Trydef collision(self, player_bbox, walls): for w in walls: result = HitTest(self, w) '''0 : No Collision, 1 : Collision from Right, 2 : Collision from Left, 3 : Collision from Top, 4 : Collision from Bottom ''' if result == 0: self.onGround = False if result == 1: self.collision_right(player_bbox, w) if result == 2: self.collision_left(player_bbox, w) if result == 3: self.collision_top(player_bbox, w) if result == 4: self.collision_bottom(player_bbox, w) if result != 0: return # leave loop
-
-
@cvp Shouldn’t those be elifs?
-
@ccc makes no difference in this case. Have tried it already.
-
@ccc You're right but I did not want to improve the Python, only a way to solve the bug. Thus I added the two lines in the same style.
-
Thanks for this information. Also read about online card generators by CardGenerator official program.