Forum Archive

Scene, Game Collision & Gravity Issues

[deleted]

Hey! I started my first little project with Pythonista, my goal at the moment is to create a working template for a 2D platformer-type game. It's more or less running fine there just some problems with the gravity and collision.

The problems are:

  • The gravity effects the player at the beginning which is fine and as soon as the player collide with the ground the variable self.onGround = True. The problem is this variable don't get reset to False after the player is not on the ground anymore which results in the player floating in the sky. As soon as I jump, the gravity is working again till I hit the ground. I tried to change the self.onGround = False in the update method but this results that other functions don't work probably.

  • The other problem is that as soon as I'm falling and move left or right the collision_left_right don't work anymore which results that the player get spawned on top of the tile when I hit it on the right or left. I guess this might due that I change velocity.xand velocity.y at the same time.

I have the bad feeling I need something like a hitTestfunction which checks where I hit the tile.
Some side bugs: LANDSCAPEdon't really work probably for me.

I'm also open for some recommendations regarding coding style, first time I work with Classes and Methods. I want the code as clean as possible.

Thanks in advance!
(Sorry for my English)

If you want to try out the code, the game is running as soon as you touch the screen.

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

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):
        #print(touches.values())
        #print(self.leftKey, self.rightKey, self.jumpKey, self.shootKey, self.onGround)
        if len(touches.values()) > 0:
            for touch in touches.values():
                if touch.location not in buttons[0].bbox or touch:
                    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):
        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_left_right(self, player_bbox, walls):
        for w in walls:
            if self.bbox.intersects(w.bbox):
                if self.velocity.x > 0:
                    new_x = w.bbox.min_x - self.bbox.w / 2
                    self.position = (new_x, self.position.y)
                    self.velocity.y = 0
                    #print("collide right")
                if self.velocity.x < 0:
                    new_x = w.bbox.max_x + self.bbox.w / 2
                    self.position = (new_x, self.position.y)
                    self.velocity.y = 0
                    #print("collide left")

    def collision_up_down(self, player_bbox, walls):
        for w in walls:
            if self.bbox.intersects(w.bbox):
                if self.velocity.y > 0:
                    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)
                if self.velocity.y < 0:
                    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)

    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. Goal is a better fitted hitbox in the future for collision.
        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)
        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
            #self.player.onGround = False
            # 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_up_down(self.player_bbox, self.walls)
            self.player.collision_left_right(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)

And level (save this as level.py):

```
level = '''
W
W
W
W
W
W
W
W
W
W
W
W
W
W
W
W
W
W WWWW
W WWW
WWWWWWWWWW WWWWWWW WWWWW WWWW
'''

cvp

@xvid when I try to run it, error AttributeError: 'Player' object has no attribute 'collision_mix' in

self.player.collision_mix
[deleted]

@cvp ups sorry, have edited now it should be working with the given code :)
If there are still some errors, hopefully not, let me know!

cvp

@xvid no errors, but I commented some prints, which, I think, crash the program because too they happen too often.
But, I don't understand the game, surely I'm too old for it 😥👴🏻

[deleted]

@cvp for real? 🤔 I commented/removed all prints, it should work in this state as no function rely on a print statement. If you have a look would be great maybe you have more experience :)

BTW it's not a "game" in this current state, just an environment to check if the collision and controls are working.

cvp

@xvid I didn't see the shoot button on my iPad mini4.
You should set your x,y in function of SCREEN_W and SCREEN_H,
like 0.9*SCREEN_W

cvp

I don't know what to do. As soon as I touch the screen, the alien falls and I don't know how to let it jump up. Sorry, but I'm not used to this kind of game...

cvp

My fault. I had incorporated the level.y into the script and forgotten to split it into lines...
I did

        lines = level#.level.splitlines()

instead of

        lines = level.splitlines()
[deleted]

@cvp ah I see! Yeah I'm using an iPad Pro 11". It could be maybe not compatible in this case with other devices. There should be a "Left", "Right", "Jump" and "Shoot" key on the device screen.

cvp

@xvid I had Understood, I had put the shoot at x=900 because 1100 is not visible for me

cvp

If I correctly understand, your first problem is that your alien stays so flying

[deleted]

@cvp Yes correct, as soon as the alien touch the ground self.onGround = True but will not change anymore expect you press jump.

cvp

@xvid is it not due to velocity.y which is 0?

cvp

Try

    def collision_up_down(self, player_bbox, walls):
        for w in walls:
            if self.bbox.intersects(w.bbox):
                if self.velocity.y > 0:
                    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)
                if self.velocity.y < 0:
                    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
        new_y = self.position.y + self.velocity.y
        if new_y > 0:
           self.onGround = False

See the return if intersects, you don't need to continue the loop because I suppose you can't intersect more than one wall

cvp

You can put the same return if intersects in the

collision_left_right
[deleted]

@cvp Thank you very much! Yeah it could be also because velocity.y is 0. Your code seem to get the gravity problem fixed but now the collision_left_right isn't working probably. You see the alien is teleported to the top of the tile if I run against it from left/rigth.

Here is a clip with what I mean: GIF

cvp

The second problem comes from that if you intercepts with a wall in collision_left_right, you will also intersects in collision_up_down and there you will set the y at up side of the wall

[deleted]

Ah you were faster haha thanks!
Yes exactly, so I kinda need a function which distinguish which collision should be triggered, right?

cvp

@xvid I think so

JonB

You need to figure out which edge of the wall is hit. Or, you should back the player up by -velocity until it clears the hit. Your code always moved to the top or left or bottom of a tile, which only works if velocity is totally horizontal or vertical. So, you need to move only in the direction of the touched edge(careful if more than one edge is touched ), or backwards along velocity might be better.

Better yet-- check for future collisions when moving the player, not after he moved.

Or, overlap of an edge is treated like a really high impulse of acceleration along the direction perpendicular to the edge, which will give you bounce. Though you'd want to also implement friction/damping of some kind.

[deleted]

@JonB I already feared I have to do something like this. I assume the method where I check for future collision might be the best but not the easiest one, right?

I probably have to check out some more advanced tutorials for this 🙃.

[deleted]

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 the Player class the jump method from if touch.location in buttons[2].bbox and self.onGround to if 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)
cvp

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
JonB

@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

[deleted]

@cvp I tried this before no good result unfortunately but thanks :)

@JonB thanks for the link! Really appreciate this, will have a look :)

cvp

@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.
Try

    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)
            if result != 0:
                return # leave loop
[deleted]

@cvp Thanks! I tried it but unfortunately this doesn't help much the player is now even more glitching. I'll read through the tutorial which @JonB mentioned maybe this give me some more insight.

ccc

@cvp Shouldn’t those be elifs?

[deleted]

@ccc makes no difference in this case. Have tried it already.

cvp

@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.

manisharma11211

Thanks for this information. Also read about online card generators by CardGenerator official program.