# Scene, Game Collision & Gravity Issues browsing

• posted
0

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.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.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)
for p in self.player_objects:
for b in self.buttons:
for b_t in self.buttons_text:
for w in self.walls:

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
0

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
``````

• posted
0

@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

• posted
0

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

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

• posted
0

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

• posted
0

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

@cvp Shouldn’t those be elifs?

• posted
0

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

• posted
0

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

Internal error.

Oops! Looks like something went wrong!