• xvid

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

    posted in Pythonista read more
  • xvid

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

    posted in Pythonista read more
  • xvid

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

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

    posted in Pythonista read more
  • xvid

    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)
    

    posted in Pythonista read more
  • xvid

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

    posted in Pythonista read more
  • xvid

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

    posted in Pythonista read more
  • xvid

    @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

    posted in Pythonista read more
  • xvid

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

    posted in Pythonista read more
  • xvid

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

    posted in Pythonista read more
  • xvid

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

    posted in Pythonista read more

Internal error.

Oops! Looks like something went wrong!