I have some questions about this game I'm making...
First, When I press F which makes the tank fire, the weapon_explosion.zip's images are all placed on top of each other and causes massive lag.
Second, After I fire lots of buggy things happen:
Firing again makes the bullet go the same direction again.
The bullet accelerates to extremely high speeds ?
(The explosion lags)
Third:
How do I make a .zip animation go once, I just went around it by destroying it after ~2.5 seconds
Now, here is my code:
PlayerImage Class (The tank):
class PlayerImage(Image):
angle = NumericProperty(0)
def __init__(self, **kwargs):
self.tuplePos = None
self.y = 122
self.eventf = None
self.eventr = None
self.source = './rpgArt/tankright.zip'
Clock.schedule_interval(lambda dt: self.gravity(), 1.0 / 60.0)
super(PlayerImage, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(self, None)
if not self._keyboard:
return
self._keyboard.bind(on_key_down=self.on_key_down)
self.gravVelocity = 1
self.bullet = ""
self.explosion = None
self.iterationBul = 0
self.left = None
self.iterations = 0
self.weapon = "./rpgArt/"
self.redtank = "./rpgArt/tankModels/tank_red/"
self.bluetank = "./rpgArt/tankModels/tank_red/"
self.redtankRight = self.redtank + "normal/tank_red_right.zip"
self.redtankLeft = self.redtank + "normal/tank_red_left.zip"
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
def on_key_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == "right" or keycode[1] == "d":
self.source = self.redtankRight
self.x += 5
if keycode[1] == "left" or keycode[1] == "a":
self.source = self.redtankLeft
self.x -= 5
if keycode[1] == "up" or keycode[1] == "spacebar" or keycode[1] == "w":
if self.source == self.redtankLeft:
self.source = self.redtankLeft
if self.source == self.redtankRight:
self.source = self.redtankRight
self.y += 50
if keycode[1] == "f":
if self.source == self.redtankLeft:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
self.source = self.redtank + "fire/tank_red_left_fire.zip"
self.tuplePos = self.x - 50, self.y - 3
self.bullet.pos = self.tuplePos # TODO POSITION BULLET
self.add_widget(self.bullet)
if self.bullet is not None:
self.eventr = Clock.schedule_interval(lambda dt: self.moveBullet(), 1.0 / 60.0) # TODO MOVE BULLET CLOCK
elif self.source == self.redtankRight:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
self.source = self.redtank + "fire/tank_red_right_fire.zip"
self.tuplePos = self.x + 50, self.y - 3
self.bullet.pos = self.tuplePos
self.add_widget(self.bullet)
if self.bullet is not None:
self.eventr = Clock.schedule_interval(lambda dt: self.moveBullet(), 1.0 / 60.0)
def moveBullet(self):
if self.iterationBul == 0:
if self.source == self.redtank + "fire/tank_red_left_fire.zip":
self.left = True
self.iterationBul = 1
elif self.source == self.redtank + "fire/tank_red_right_fire.zip":
self.left = False
self.iterationBul = 1
else:
if self.left is True:
self.eventf = Clock.schedule_interval(lambda dt: self.movebulletleft(), 5.0 / 20.0)
if self.left is False:
self.eventf = Clock.schedule_interval(lambda dt: self.movebulletright(), 5.0 / 20.0)
def movebulletleft(self):
if self.bullet is None:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
else:
x, y = self.tuplePos
if x < 0:
self.explosion = Image(source=self.weapon + "weapon_explosion.zip")
self.explosion.pos = x, y
self.add_widget(self.explosion)
self.remove_widget(self.bullet)
Clock.unschedule(self.eventf)
Clock.unschedule(self.eventr)
self.iterationBul = 0
self.left = None
self.bullet = None
Clock.schedule_once(lambda dt: self.REexplosion(), 5)
else:
self.bullet.pos = (x - 3, y)
self.tuplePos = self.bullet.pos
print('left is true')
print(str(self.bullet.pos) + " bullet pos")
def REexplosion(self):
self.remove_widget(self.explosion)
def movebulletright(self):
if self.bullet is None:
self.bullet = Image(source=self.weapon + "weapon_bullet.zip")
else:
self.bullet.pos = (x, y) = self.tuplePos
if x > 680:
self.explosion = Image(source=self.weapon + "weapon_explosion.zip")
self.explosion.pos = x, y
self.add_widget(self.explosion)
self.remove_widget(self.bullet)
Clock.unschedule(self.eventf)
Clock.unschedule(self.eventr)
self.iterationBul = 0
self.left = None
self.bullet = None
Clock.schedule_once(lambda dt: self.REexplosion(), 1.7)
else:
v = 3
self.bullet.pos = (x + v, y)
self.tuplePos = self.bullet.pos
print('right is true')
try:
print(str(self.bullet.pos) + " bullet pos")
except AttributeError:
print("bullet out of range!")
def gravity(self):
if self.y == 123:
pass
else:
self.y -= 2
if self.y <= 122:
self.y += 1
# if self.iterations / 60 != 0:
# result = self.iterations / 60
# self.gravVelocity += result
# print(self.y)
def on_touch_down(self, touch):
# self.currentstate = self.states["person.zip/"]
Animation.cancel_all(self)
angle = degrees(atan2(touch.y - self.center_y,
touch.x - self.center_x))
Animation(center=touch.pos, angle=angle).start(self)
# self.currentstate = self.states["personred/rest.png/"]
print(self.y)
Also, my Builder.load_strings:
root = Builder.load_string('''
Widget:
Widget:
canvas:
Color:
rgb: 0,0,1
Rectangle:
size: (900,500)
pos: (0,150)
Widget:
Image:
pos: (0,150)
size: (400,400)
source: './rpgArt/city.zip'
Widget:
canvas:
Color:
rgb: 0,1,0
Rectangle:
size: (900,150)
pos: (0,0)
PlayerImage:
source: './rpgArt/tankModels/tank_red/normal/tank_red_right.zip'
allow_stretch: True
keep_ratio: True
pos:(0,130)
''')
runTouchApp(root)
And the other one which I don't fully understand... (From someone else.)
Builder.load_string('''
<PlayerImage>:
canvas.before:
PushMatrix
canvas.after:
PopMatrix
''')
I needed to unschedule self.eventr at movebullet(self):.
Clock.unschedule(self.eventr)
Related
I was writing a simple ping pong game as an introduction to arcade and in the loop on line 51. It is supposed to stop the sprites moving but it just isn't happening. I tried to remove the self., but got an error, tried replacing self. with arcade., but still getting an error.
import arcade
screen_width = 900
screen_height = 800
screen_title = 'Pingpong'
class MainGame(arcade.Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)
self.ball = ball('ball.png', 0.4)
self.bar = bar('barman.png', 0.5)
self.live = 3
self.score = 0
def setup(self):
self.ball.center_x = 450
self.ball.center_y = 400
self.ball.stepx = 15
self.ball.stepy = 15
self.bar.center_x = 400
self.bar.center_y = 70
self.bar.stepx = 0
def on_draw(self):
arcade.start_render()
self.ball.draw()
self.bar.draw()
arcade.set_background_color(arcade.color.DARK_GRAY)
life = f'Жизней: {self.live}'
scrore = f'Счёт: {self.score}'
arcade.draw_text(life, 750, 740, arcade.color.BLACK, 20)
arcade.draw_text(scrore, 10, 740, arcade.color.BLACK, 20)
if self.live == 0:
arcade.draw_text('Вы лох', 400, 400, arcade.color.RED_DEVIL, 60)
arcade.finish_render()
def update(self, delta_time: float):
self.ball.update()
self.bar.update()
colliding = arcade.check_for_collision(self.ball, self.bar)
if colliding:
self.ball.bottom = self.example.top
self.ball.stepy = -self.ball.stepy
self.score += 1
if self.ball.bottom < 0:
self.live -= 1
self.ball.center_x = 450
self.ball.center_y = 400
if self.live == 0:
ball.stop()
bar.stop()
def on_key_press(self, key, modifiers):
if key == arcade.key.A:
self.bar.stepx = -15
if key == arcade.key.D:
self.bar.stepx = 15
def on_key_release(self, key, modifiers):
if key == arcade.key.A or key == arcade.key.D:
self.bar.stepx = 0
class ball(arcade.Sprite):
def update(self):
self.center_x += self.stepx
self.center_y += self.stepy
if self.bottom < 0 or self.top > screen_height:
self.stepy = -self.stepy
if self.left < 0 or self.right > screen_width:
self.stepx = -self.stepx
class bar(arcade.Sprite):
def update(self):
self.center_x += self.stepx
if self.left < 0 or self.right > screen_width:
self.stepx = 0
game = MainGame(screen_width, screen_height, screen_title)
game.setup()
arcade.run()
I tried removing the self. and got an error, tried replacing the self. with an arcade, got an error too. The loop works too it seems
Calling stop doesn't make sense in your code because at the same time you're updating your sprites coordinates. You need to update ball and bar only wnen live is above 0:
import arcade
screen_width = 900
screen_height = 800
screen_title = 'Pingpong'
class MainGame(arcade.Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)
self.ball = ball('ball.png', 0.4)
self.bar = bar('barman.png', 0.5)
self.live = 3
self.score = 0
def setup(self):
self.ball.center_x = 450
self.ball.center_y = 400
self.ball.stepx = 15
self.ball.stepy = 15
self.bar.center_x = 400
self.bar.center_y = 70
self.bar.stepx = 0
def on_draw(self):
arcade.start_render()
self.ball.draw()
self.bar.draw()
arcade.set_background_color(arcade.color.DARK_GRAY)
life = f'Жизней: {self.live}'
score = f'Счёт: {self.score}'
arcade.draw_text(life, 750, 740, arcade.color.BLACK, 20)
arcade.draw_text(score, 10, 740, arcade.color.BLACK, 20)
if self.live == 0:
arcade.draw_text('Вы лох', 400, 400, arcade.color.RED_DEVIL, 60)
arcade.finish_render()
def update(self, delta_time: float):
if self.live > 0:
self.ball.update()
self.bar.update()
colliding = arcade.check_for_collision(self.ball, self.bar)
if colliding:
self.ball.bottom = self.bar.top
self.ball.stepy = -self.ball.stepy
self.score += 1
if self.ball.bottom < 0:
self.live -= 1
self.ball.center_x = 450
self.ball.center_y = 400
def on_key_press(self, key, modifiers):
if key == arcade.key.A:
self.bar.stepx = -15
if key == arcade.key.D:
self.bar.stepx = 15
def on_key_release(self, key, modifiers):
if key == arcade.key.A or key == arcade.key.D:
self.bar.stepx = 0
class ball(arcade.Sprite):
def update(self):
self.center_x += self.stepx
self.center_y += self.stepy
if self.bottom < 0 or self.top > screen_height:
self.stepy = -self.stepy
if self.left < 0 or self.right > screen_width:
self.stepx = -self.stepx
class bar(arcade.Sprite):
def update(self):
self.center_x += self.stepx
if self.left < 0 or self.right > screen_width:
self.stepx = 0
game = MainGame(screen_width, screen_height, screen_title)
game.setup()
arcade.run()
Output:
I have a assignment that ask me to make a snake game with Python. The assignment asks me to make the snake's body gets trimmed when its head touches the body.
The snake body is a queue, so here is the code for forming a queue:
The snake's head is defined as the rear node and the snake's tail is defined as the front node, as shown in the picture below.
#The code of forming a queue, it cannot be modified
class Node:
def __init__(self, x, y):
self.x = x
self.y = y
self.pre = None
self.next = None
class Queue:
def __init__(self):
self.front = None
self.rear = None
def len(self):
length = 0
cur = self.front
while cur:
cur = cur.next
length += 1
return length
def enQueue(self, x, y):
new = Node(x, y)
if self.len() == 0:
self.front = new
self.rear = new
else:
new.pre = self.rear
self.rear.next = new
self.rear = new
def deQueue(self):
if self.len() <= 1:
self.front = None
self.rear = None
else:
self.front = self.front.next
self.front.pre = None
def reverse(self):
cur = self.front
self.rear, self.front = self.front , self.rear
while cur:
cur.next, cur.pre = cur.pre, cur.next
cur = cur.pre
def printQueue(self):
cur = self.front
print("front", end=" ")
while cur:
print(f"[{cur.x}, {cur.y}]", end=" ")
cur = cur.next
print("rear")
And this is the main code for the game:
import pygame, sys, time, random
from pygame.locals import *
from pathlib import Path, os
from coor_queue import Node, Queue
from item_stack import Stack
class SnakeGame:
def __init__(self):
self.g = 30 #The width of each grid
self.dir = "right" #Initial Direction
self.snake = Queue() #Queue of the snake
for i in range(9):
self.snake.enQueue(i*self.g,9*self.g)
self.init_params()
self.init_pygame()
self.init_objects()
self.init_images()
#The code above cannot be modified
# =========================== Movement ===========================
def move(self):
headx = self.snake.rear.x
heady = self.snake.rear.y
if self.dir == "down":
heady += self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "up":
heady -= self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "left":
headx -= self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
if self.dir == "right":
headx += self.g
self.snake.enQueue(headx, heady)
self.snake.deQueue()
# =========================== Add Tails ===========================
def add_tail(self):
tailx = self.snake.front.x
taily = self.snake.front.y
newtail = Node(tailx, taily)
newtail.next = self.snake.front
self.snake.front.pre = newtail
self.snake.front = newtail
# =========================== Trim the snake's body ===========================
def eat_body(self):
# If the snake's head touches a grid (node) of the body
# Then the part from original tail to the grid which the snake's head touches
# Hint: Use self.snake
# ============The code below cannot be modified======================
def main(self):
while True:
self.keyboard_input()
self.check_input_valid()
self.move()
self.eat()
self.display()
if self.is_dead():
self.game_over()
self.fps.tick(8 + self.speed//5)
def keyboard_input(self): # Keyboard input
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == ord("d") or event.key == K_RIGHT: self.input_dir = "right"
if event.key == ord("a") or event.key == K_LEFT: self.input_dir = "left"
if event.key == ord("w") or event.key == K_UP: self.input_dir = "up"
if event.key == ord("s") or event.key == K_DOWN: self.input_dir = "down"
if event.key == K_ESCAPE: pygame.event.post(pygame.event.Event(QUIT))
def check_input_valid(self): #If the input direction is the opposite of the original direction, the input will be invalid
if (self.input_dir == "right" and self.dir != "left") or (self.input_dir == "left" and self.dir != "right") or \
(self.input_dir == "up" and self.dir != "down") or (self.input_dir == "down" and self.dir != "up"):
self.dir = self.input_dir
def eat(self): #Eat body or food
self.eat_food()
self.eat_body()
def display(self):
#Background
self.blit_map()
#Snake body
cur = self.snake.rear.pre
while cur:
self.blit_image("body", cur.x, cur.y, self.g, self.g)
cur = cur.pre
#Snake head
if self.snake.rear.x < self.width:
self.blit_image("head", self.snake.rear.x, self.snake.rear.y, self.g, self.g)
#Food
self.blit_image(self.food, self.foodPos.x, self.foodPos.y, self.g, self.g)
#Status Bar
self.blit_status_bar()
pygame.display.flip()
def is_dead(self):
return (self.snake.rear.x == self.width or self.snake.rear.x < 0) or \
(self.snake.rear.y == self.height or self.snake.rear.y < 0)
def game_over(self): # Game Over
self.play_theme("game_over1")
time.sleep(3)
pygame.quit()
sys.exit()
def init_params(self): #Basic parameters
self.screen_width = 1140
self.screen_height = 540
self.width = 960
self.height = 540
self.speed = 0
self.prob = 25
self.score = 0
self.spf = 10
self.satiety = 0
self.input_dir = None
def init_pygame(self): #Initialize pygame
pygame.init()
self.fps = pygame.time.Clock() #
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height)) #
pygame.display.set_caption("Snake Game") #
pygame.mixer.init()
def init_objects(self):
self.food_list = [food.split(".")[0] for food in os.listdir(Path("src/image/food"))]
self.food = None
self.select_food()
self.foodPos = Node(self.width//2, self.height//2)
def init_images(self):
self.img_StatusBar = pygame.image.load(Path("src/image/other/StatusBar.jpg")).convert_alpha()
self.img_head = pygame.image.load(Path("src/image/snake/SnakeHead.jpg")).convert_alpha()
self.img_body = pygame.image.load(Path("src/image/snake/SnakeBody.jpg")).convert_alpha()
self.img_map = pygame.image.load(Path("src/image/map/Map1.jpg")).convert_alpha()
for food in self.food_list:
exec(f"self.img_{food} = pygame.image.load(Path('src/image/food/{food}.jpg')).convert_alpha()")
def eat_food(self):
if self.snake.rear.x == self.foodPos.x and self.snake.rear.y == self.foodPos.y:
self.satiety += 1
self.speed += 1
self.spf = 10 + ((self.satiety-1)//10)*10
self.score += self.spf
self.play_effect("eat_food")
x = random.randrange(1, self.width//self.g)
y = random.randrange(1, self.height//self.g)
self.foodPos.x = x*self.g
self.foodPos.y = y*self.g
self.select_food()
self.add_tail()
def select_food(self):
while True:
next_food = self.food_list[random.randrange(len(self.food_list))]
if next_food != self.food:
self.food = next_food
break
def play_theme(self, theme):
pygame.mixer.music.load(Path(f"src/sound/theme/{theme}.mp3"))
pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play()
def play_effect(self, effect):
sound = pygame.mixer.Sound(Path(f"src/sound/effect/{effect}.mp3"))
sound.set_volume(0.7)
sound.play()
def blit_image(self, name, coor_x = 0, coor_y = 0, size_x = 0, size_y = 0):
exec(f"self.img_{name} = pygame.transform.scale(self.img_{name}, size=(size_x, size_y))")
exec(f"self.screen.blit(self.img_{name}, (coor_x, coor_y))")
def blit_rect(self, data, coor_x = 0, coor_y = 0, font_size = 80, color = (0, 0, 0)):
self.Font = pygame.font.SysFont("", font_size)
self.Surf = self.Font.render(str(data), True, color)
self.Rect = self.Surf.get_rect()
self.Rect.center = (coor_x, coor_y)
self.screen.blit(self.Surf, self.Rect)
def blit_map(self):
self.blit_image("map", 0, 0, self.width, self.height)
def blit_status_bar(self):
self.blit_image("StatusBar", 960, 0, 180, self.screen_height)
self.blit_rect(self.score, 35*self.g, 1.87*self.g, font_size = 50, color = (238, 0, 0))
self.blit_rect(int(8 + self.speed)-7, 35*self.g, 4.13*self.g, font_size = 50, color = (178, 58, 238))
self.blit_rect(self.snake.len(), 35*self.g, 6.28*self.g, font_size = 50, color = (50, 205, 50))
# ==================================================================
game = SnakeGame()
game.main()
The picture below explains how trimming snake body works, the Python logo is the head.
I understand that the condition of trimming the body is when the head reaches the same grid as a part of the body. But I have no idea how to write this condition because I'm not sure if there is a way to get the coordinate from the nodes between front node and rear node.
Some further explanation is appreciated, thanks in advance.
Use a loop to compare the head of the queue against all elements of the body. The loop is similar to the loop in the print method. Return true if the position of the head is equal to a body position, else return false:
def isHeadEqualAnyBodyItem(snake):
head = snake.front
if not head:
return False
body = head.next
while body:
if head.x == body.x and head.y == body.y
return True
body = body.next
return False
My Kivy app shows on left bottom of screen of phone (You can see in shared screenshot).But I want to make it fullscreen.I make value of "fullscreen" equal to 1 in "buildozer.spec" file.But nothing changed.Here is my code:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.uix.behaviors import ButtonBehavior
from kivy.core.audio import SoundLoader
Window.size = (320,640)
opart = False
vpart = False
rpart = False
och = False
vch = False
rch = False
ofin = False
vfin = False
rfin = False
fin = True
snd_part = SoundLoader.load("part.wav")
music = SoundLoader.load("music.wav")
music.play()
class Orange(ButtonBehavior,Image):
def __init__(self,**kwargs):
super(Orange,self).__init__(**kwargs)
self.source = "obal.png"
self.size = (85,113)
self.pos = (32,300)
def on_press(self):
global opart
opart = True
Clock.schedule_interval(self.todown,0.1)
def todown(self,*args):
global och
if not och:
snd_part.play()
self.source = "effect.png"
och = True
else:
self.x = 28
self.source = "esma.png"
if self.y > 250:
self.y -= 4
else:
self.y = 250
global ofin,fin
ofin = True
if vfin and rfin and fin:
fin = False
win.kill_platform()
class Violet(ButtonBehavior,Image):
def __init__(self,**kwargs):
super(Violet,self).__init__(**kwargs)
self.source = "vbal.png"
self.size = (85,113)
self.pos = (117,364)
def on_press(self):
global vpart
vpart = True
Clock.schedule_interval(self.todown,0.1)
def todown(self,*args):
global vch
if not vch:
snd_part.play()
self.source = "effect.png"
vch = True
else:
self.x = 113
self.source = "yaxsiki.png"
if self.y > 250:
self.y -= 4
else:
self.y = 250
global vfin,fin
vfin = True
if ofin and rfin and fin:
fin = False
win.kill_platform()
class Red(ButtonBehavior,Image):
def __init__(self,**kwargs):
super(Red,self).__init__(**kwargs)
self.source = "rbal.png"
self.size = (85,113)
self.pos = (202,300)
def on_press(self):
global rpart
rpart = True
Clock.schedule_interval(self.todown,0.1)
def todown(self,*args):
global rch
if not rch:
snd_part.play()
self.source = "effect.png"
rch = True
else:
self.x = 198
self.source = "varsan.png"
if self.y > 250:
self.y -= 4
else:
self.y = 250
global rfin,fin
rfin = True
if ofin and vfin and fin:
fin = False
win.kill_platform()
class Platform(Widget):
def __init__(self,**kwargs):
super(Platform,self).__init__(**kwargs)
self.size = (320,640)
self.bgimage = Image(source = "bg.jpg")
self.bgimage.size = (320,640)
self.obeta = False
self.vbeta = False
self.rbeta = False
self.obal = Orange()
self.vbal = Violet()
self.rbal = Red()
Clock.schedule_interval(self.moveOrange,0.1)
Clock.schedule_interval(self.moveViolet,0.1)
Clock.schedule_interval(self.moveRed,0.1)
self.bgimage.add_widget(self.obal)
self.bgimage.add_widget(self.vbal)
self.bgimage.add_widget(self.rbal)
self.add_widget(self.bgimage)
def moveOrange(self,*args):
if not opart:
if not self.obeta:
self.obal.y += 2
else:
self.obal.y -= 2
if self.obal.y <= 280:
self.obeta = False
if self.obal.y >= 320:
self.obeta = True
def moveViolet(self,*args):
if not vpart:
if not self.vbeta:
self.vbal.y += 1
else:
self.vbal.y -= 1
if self.vbal.y <= 344:
self.vbeta = False
if self.vbal.y >= 384:
self.vbeta = True
def moveRed(self,*args):
if not rpart:
if not self.rbeta:
self.rbal.y += 3
else:
self.rbal.y -= 3
if self.rbal.y <= 280:
self.rbeta = False
if self.rbal.y >= 320:
self.rbeta = True
class Platform2(Widget):
def __init__(self,**kwargs):
super(Platform2,self).__init__(**kwargs)
self.size = (320,640)
self.bgimage = Image(source = "bg.jpg")
self.bgimage.size = (320,640)
self.add_widget(self.bgimage)
class MainApp(App):
def build(self):
self.layout = GridLayout()
self.platform = Platform()
self.layout.add_widget(self.platform)
return self.layout
def kill_platform(self):
self.platform = Platform2()
self.layout.add_widget(self.platform)
win = MainApp()
win.run()
Here you can see screenshot.That's how it shows on my phone:
enter image description here
Replace the Window.size = (320,640) line with:
from kivy.utils import platform
if platform not in ["android", "ios"]:
Window.size = (320,640)
Side notes, you might wanna change the attributes that use the 320x640 pixel count or at least smaller than it to something like size_hint=(1,1). This answer might be unclear for most, but this is the best I can do
I have an rotated rectangle, which changes it's position a bit when I resize it.
but I dont want that it moves...
this position change happens, when I reset the center, if I dont change the center, it doesnt move but its important for me that the center is correct.
I created 2 videos.
this is it is currently: https://youtu.be/TqY3Ji0rnLw
and this is how I want that it is (I removed the center reset, but as I said before I said that I want a center reset): https://youtu.be/Jfq777nzu6o
how can I solve this?
this is my code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle, PushMatrix, Rotate, PopMatrix
from kivy.clock import Clock
from kivy.core.window import Window
class Canvas:
def __init__(self, root, pos, size, rotation=0, color=(1, 0, 0, 1)):
with root.canvas:
PushMatrix()
self._color_instruction = Color(rgba=color)
self._rotation_instruction = Rotate(angle=rotation, origin=(150, 150))
self._rectangle_instruction = Rectangle(pos=pos, size=size)
PopMatrix()
#property
def center(self):
pos = self.pos
size = self.size
center_x = pos[0] + size[0] / 2
center_y = pos[1] + size[1] / 2
return (center_x, center_y)
#property
def pos(self):
return self._rectangle_instruction.pos
#pos.setter
def pos(self, value):
self._rectangle_instruction.pos = value
self._rotation_instruction.origin = self.center
#property
def size(self):
return self._rectangle_instruction.size
#size.setter
def size(self, value):
self._rectangle_instruction.size = value
self._rotation_instruction.origin = self.center
#property
def rotation(self):
return self._rotation_instruction.angle
#rotation.setter
def rotation(self, value):
self._rotation_instruction.angle = value
#property
def color(self):
return self._color_instruction.rgba
#color.setter
def color(self, value):
self._color_instruction.rgba = value
class Root(Widget):
def __init__(self, **kwargs):
super(Root, self).__init__(**kwargs)
self.rectangle = Canvas(self, (100, 100), (100, 100), rotation=45)
self.keys_pressed = set()
self._keyboard = Window.request_keyboard(self._on_keyboard_closed, self)
self._keyboard.bind(on_key_down = self._on_key_down)
self._keyboard.bind(on_key_up = self._on_key_up)
Clock.schedule_interval(self.step, 0)
def step(self, dt):
x = self.rectangle.pos[0]
y = self.rectangle.pos[1]
width = self.rectangle.size[0]
height = self.rectangle.size[1]
rotation = self.rectangle.rotation
step_size = 300 * dt
if "up" in self.keys_pressed:
y += step_size
if "left" in self.keys_pressed:
x -= step_size
if "right" in self.keys_pressed:
x += step_size
if "down" in self.keys_pressed:
y -= step_size
if "y" in self.keys_pressed:
rotation += step_size
if "x" in self.keys_pressed:
rotation -= step_size
if "w" in self.keys_pressed:
height = min(height + step_size, 300)
if "a" in self.keys_pressed:
width = max(width - step_size, 10)
if "d" in self.keys_pressed:
width = min(width + step_size, 300)
if "s" in self.keys_pressed:
height = max(height - step_size, 10)
self.rectangle.pos = (x, y)
self.rectangle.size = (width, height)
self.rectangle.rotation = rotation
def _on_keyboard_closed(self):
self._keyboard.unbind(_on_key_down = self._on_key_down)
self._keyboard.ubind(_on_key_up = self._on_key_up)
self._keyboard = None
def _on_key_down(self, keyboard, keycode, text, modifiers):
self.keys_pressed.add(text if text != None else keycode[1])
def _on_key_up(self, keyboard, keycode):
if keycode[1] in self.keys_pressed:
self.keys_pressed.remove(keycode[1])
class TestApp(App):
def __init__(self, **kwargs):
super(TestApp, self).__init__(**kwargs)
def build(self):
return Root()
def main():
app = TestApp()
app.run()
if __name__ == "__main__":
main()
how do I have to change the position that its like in the 2nd vid?
Here is a modified version of your code that uses kv and the existing canvas object to draw the Rectangle:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ListProperty, NumericProperty
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.core.window import Window
class MyCanvas(Widget):
color = ListProperty([1,0,0,1])
rotation = NumericProperty(0)
Builder.load_string('''
<MyCanvas>:
size_hint: None, None
canvas:
Color:
rgba: self.color
PushMatrix:
Translate:
xy: self.x, self.y
Rotate:
axis: 0,0,1
origin: 0,0
angle: self.rotation
Scale:
origin: 0,0
xyz: self.width, self.height, 1
Rectangle: # unit rectangle centered on origin
pos: -0.5, -0.5
size: 1, 1
PopMatrix:
''')
class Root(Widget):
def __init__(self, **kwargs):
super(Root, self).__init__(**kwargs)
# self.rectangle = Canvas(self, (100, 100), (100, 100), rotation=45)
self.rectangle = MyCanvas(pos=(100,100), size=(100,100), rotation=45)
self.add_widget(self.rectangle)
self.keys_pressed = set()
self._keyboard = Window.request_keyboard(self._on_keyboard_closed, self)
self._keyboard.bind(on_key_down = self._on_key_down)
self._keyboard.bind(on_key_up = self._on_key_up)
Clock.schedule_interval(self.step, 0)
def step(self, dt):
x = self.rectangle.pos[0]
y = self.rectangle.pos[1]
width = self.rectangle.size[0]
height = self.rectangle.size[1]
rotation = self.rectangle.rotation
step_size = 300 * dt
if "up" in self.keys_pressed:
y += step_size
if "left" in self.keys_pressed:
x -= step_size
if "right" in self.keys_pressed:
x += step_size
if "down" in self.keys_pressed:
y -= step_size
if "y" in self.keys_pressed:
rotation += step_size
if "x" in self.keys_pressed:
rotation -= step_size
if "w" in self.keys_pressed:
height = min(height + step_size, 300)
if "a" in self.keys_pressed:
width = max(width - step_size, 10)
if "d" in self.keys_pressed:
width = min(width + step_size, 300)
if "s" in self.keys_pressed:
height = max(height - step_size, 10)
self.rectangle.pos = (x, y)
self.rectangle.size = (width, height)
self.rectangle.rotation = rotation
def _on_keyboard_closed(self):
self._keyboard.unbind(_on_key_down = self._on_key_down)
self._keyboard.ubind(_on_key_up = self._on_key_up)
self._keyboard = None
def _on_key_down(self, keyboard, keycode, text, modifiers):
self.keys_pressed.add(text if text != None else keycode[1])
def _on_key_up(self, keyboard, keycode):
if keycode[1] in self.keys_pressed:
self.keys_pressed.remove(keycode[1])
class TestApp(App):
def __init__(self, **kwargs):
super(TestApp, self).__init__(**kwargs)
def build(self):
return Root()
def main():
app = TestApp()
app.run()
if __name__ == "__main__":
main()
The sizing of the Rectangle does not behave exactly as your video shows, but it is close.
You can get the sizing behavior that you want by changing the kv to:
<MyCanvas>:
size_hint: None, None
canvas.before:
PushMatrix:
Rotate:
axis: 0,0,1
origin: self.pos
angle: self.rotation
canvas:
Color:
rgba: self.color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
PopMatrix:
But this affects the rotation behavior so that the rotation is about a corner of the Rectangle instead of the center.
I am making a python game and I don't know what should I set my FPS to. My game is stuck and not smooth. How do I know what should the FPS be?
This is my code:
http://bin.shortbin.eu:8080/x87bSUKUiN
After running your code I have also noticed frame rate drops which hurt the smoothness of the game.
There are two separate issues here:
1. The FPS drops
The FPS drops probably happen because of something you cannot control, like the garbage collector working.
Even though you have no control of such issues, you can in general improve the performance of your game. See the following screen shot of a profiler run of your game:
You can see that the largest part of the time is spent on blit. However, a very large part of the time is also being spent in get_y_list. The get_y_list method also uses large lists which create a lot of garbage for the garbage collector to later collect, thus causing a further performance hit.
As I understand it, the get_y_list method is part of a very crude method that you're using for collision detection, one which basically takes quadratic time. Your algorithm seems to divide each object into a large amount of 2d cells, and then test collision between each pair of cells. Instead you can just test box to box intersection. If you wish to have a complicated collision shape for the objects you can use other algorithms, the internet is full of them. See this for example: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
Using other algorithms for collision detection will greatly improve your performance.
2. The game becoming non-smooth when there's an FPS drop.
The x and y positions of your objects are being updated like this for example: player.x -= player.vx. The physically correct method would be: player.x -= player.vx * DELTA_T. Where DELTA_T is the time elapsed since the last frame. If you don't use the time elapsed since the last frame, the speed of motion of your characters becomes dependent on FPS. Which is exactly what we're seeing.
Where do I get DELTA_T you might ask. You do it directly when calling tick:
def main():
global DELTA_T
finish = False
background_x = 0
background_y = 0
background = pygame.image.load(BACKGROUND_IAMGE)
while not finish:
DELTA_T = clock.tick(REFRESH_RATE)
I tried adding the multiplication by DELTA_T but the game becomes unstable, because you assume that objects advance exactly vx 'cells' in each frame to detect collisions and floor penetrations. See my attempt below:
import pygame
pygame.init()
WINDOW_WIDTH = 700
WINDOW_HEIGHT = 500
SIZE = (WINDOW_WIDTH, WINDOW_HEIGHT)
SCREEN = pygame.display.set_mode(SIZE)
pygame.display.set_caption("Python Game")
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PINK = (255, 0, 255)
BACKGROUND_IAMGE = 'back.png'
clock = pygame.time.Clock()
REFRESH_RATE = 30
FRAMES_DELAY = 3
PLAYER_HEIGHT = 72
PLAYER_WIDTH = 40
MUSHROOM_HEIGHT = 31
MUSHROOM_WIDTH = 40
BLOCK_HEIGHT = 30
BLOCK_WIDTH = 30
FLOOR_Y = 435
DELTA_T = 0
class Player:
def __init__(self, x, y):
self.__images = [pygame.image.load('mario1.png'), pygame.image.load('mario2.png'),
pygame.image.load('mario3.png'), pygame.image.load('mario4.png'),
pygame.image.load('mario5.png'), pygame.image.load('mario6.png')]
self.__current_image = self.__images[0]
self.x = x
self.y = y
self.vx = 5/33.
self.vy = 10/33.
self.__is_mid_air = False
self.__direction = "right"
self.__is_jumping = False
self.__MAX_JUMP = 20
self.__is_walking = False
self.__counter = 0
self.__jump_counter = 0
def reset_jump_counter(self):
self.__jump_counter = 0
def get_bottom_y(self):
return int(self.y + PLAYER_HEIGHT)
def set_walk(self, bol):
self.__is_walking = bol
def set_is_mid_air(self, bol):
self.__is_mid_air = bol
def is_mid_air(self):
return self.__is_mid_air
def get_image(self):
if self.__is_mid_air and self.__direction == "right":
self.__current_image = self.__images[4]
return self.__current_image
if self.__is_mid_air and self.__direction == "left":
self.__current_image = self.__images[5]
return self.__current_image
self.__counter += 1
if self.__counter > FRAMES_DELAY:
self.__counter = 0
if self.__counter == FRAMES_DELAY and self.__direction == "right":
if self.__is_walking and self.__current_image == self.__images[0]:
self.__current_image = self.__images[1]
else:
self.__current_image = self.__images[0]
if self.__counter == FRAMES_DELAY and self.__direction == "left":
if self.__is_walking and self.__current_image == self.__images[2]:
self.__current_image = self.__images[3]
else:
self.__current_image = self.__images[2]
return self.__current_image
def stand_still(self):
if self.__direction == "right":
self.__current_image = self.__images[0]
if self.__direction == "left":
self.__current_image = self.__images[2]
def set_jump(self, bol):
self.__is_jumping = bol
if not bol:
self.reset_jump_counter()
def check_jump(self):
if self.__jump_counter != self.__MAX_JUMP and self.__is_jumping:
self.y -= self.vy * DELTA_T
self.__jump_counter += 1
if self.__jump_counter >= self.__MAX_JUMP:
self.__is_jumping = False
self.__jump_counter = 0
def is_jumping(self):
return self.__is_jumping
def fall(self):
if not self.__is_jumping:
self.y += self.vy * DELTA_T
def get_direction(self):
return self.__direction
def turn_around(self):
if self.__direction == "right":
self.__direction = "left"
elif self.__direction == "left":
self.__direction = "right"
def get_x_list(self):
result = []
for i in range(PLAYER_WIDTH + 1):
result.append(self.x + i)
return result
def get_y_list(self):
result = []
for i in range(PLAYER_HEIGHT + 1):
result.append(self.y + i)
return result
def get_right_x(self):
return self.x + PLAYER_WIDTH
def is_crash(self, obj):
is_x_equals = False
for i in range(int(self.vx * DELTA_T+0.5)):
if self.x + PLAYER_WIDTH + i in obj.get_x_list():
is_x_equals = True
if self.x - i in obj.get_x_list():
is_x_equals = True
is_y_equals = False
for i in range(int(self.vy*DELTA_T+0.5)):
if self.y + PLAYER_HEIGHT + i in obj.get_y_list():
is_y_equals = True
if self.y - i in obj.get_y_list():
is_y_equals = True
return is_x_equals and is_y_equals
class Monster:
def __init__(self, x, y, vx, vy, monster_type):
self.__images = [pygame.image.load(monster_type + '1.png').convert(),
pygame.image.load(monster_type + '2.png').convert()]
if monster_type == "mushroom":
self.__width = MUSHROOM_WIDTH
self.__height = MUSHROOM_HEIGHT
self.__current_image = self.__images[0]
self.__FIRST_X = x
self.__FIRST_Y = y
self.__x = x
self.__y = y
self.__vx = vx
self.__vy = vy
self.__direction = "left"
self.__monster_type = monster_type
self.__counter = 0
def get_image(self):
self.__counter += 1
if self.__counter > FRAMES_DELAY:
self.__counter = 0
if self.__monster_type == "mushroom" and self.__counter == FRAMES_DELAY:
if self.__current_image == self.__images[1]:
self.__current_image = self.__images[0]
self.__current_image.set_colorkey(PINK)
else:
self.__current_image = self.__images[1]
self.__current_image.set_colorkey(PINK)
return self.__current_image
elif self.__monster_type == "mushroom" and self.__counter != FRAMES_DELAY:
self.__current_image.set_colorkey(PINK)
return self.__current_image
def get_x(self):
return self.__x
def get_vx(self):
return self.__vx
def get_vy(self):
return self.__vx
def get_y(self):
return self.__y
def step(self):
if self.__direction == "right":
self.__x += self.__vx * DELTA_T
elif self.__direction == "left":
self.__x -= self.__vx * DELTA_T
def get_direction(self):
return self.__direction
def turn_around(self):
if self.__direction == "right":
self.__direction = "left"
elif self.__direction == "left":
self.__direction = "right"
def get_x_list(self):
result = []
for i in range(MUSHROOM_WIDTH + 1):
result.append(self.__x + i)
return result
def get_y_list(self):
result = []
for i in range(MUSHROOM_HEIGHT + 1):
result.append(self.__y + i)
return result
def is_crash(self, obj):
is_x_equals = False
for i in range(int(self.__vx * DELTA_T+0.5)):
if self.__x + self.__width + i in obj.get_x_list():
is_x_equals = True
if self.__x - i in obj.get_x_list():
is_x_equals = True
is_y_equals = False
for i in range(int(self.__vy * DELTA_T+0.5)):
if self.__y + self.__height + i in obj.get_y_list():
is_y_equals = True
if self.__y - i in obj.get_y_list():
is_y_equals = True
return is_x_equals and is_y_equals
class Block:
def __init__(self, x, y, width=1, height=1):
self.__image = pygame.image.load("block.png")
self.__x = x
self.__y = y
self.__width = width
self.__height = height
def load_image(self, background_x):
for i in range(self.__width):
for j in range(self.__height):
SCREEN.blit(self.__image, (self.__x + (i * BLOCK_WIDTH) + background_x, self.__y + (j * BLOCK_HEIGHT)))
def get_x_list(self):
result = []
for i in range(BLOCK_WIDTH * self.__width + 1):
result.append(self.__x + i)
return result
def get_y_list(self):
result = []
for i in range(BLOCK_HEIGHT * self.__height + 1):
result.append(self.__y + i)
return result
def get_y(self):
return self.__y
def get_x(self):
return self.__x
def get_width(self):
return self.__width
def get_height(self):
return self.__height
def get_bottom_y(self):
return self.__y + (BLOCK_HEIGHT * self.__height)
def get_right_x(self):
return self.__x + self.__width * BLOCK_WIDTH
player = Player(140, FLOOR_Y - PLAYER_HEIGHT)
blocks = [Block(270, 280, 1, 1), Block(301, FLOOR_Y - BLOCK_HEIGHT * 8, 1, 8),
Block(600, FLOOR_Y - BLOCK_HEIGHT * 8, 2, 8)]
monsters = [Monster(380, FLOOR_Y - MUSHROOM_HEIGHT, 3/33., 3/33., "mushroom")]
def main():
global DELTA_T
finish = False
background_x = 0
background_y = 0
background = pygame.image.load(BACKGROUND_IAMGE)
while not finish:
DELTA_T = clock.tick(REFRESH_RATE)
SCREEN.blit(background, (background_x, background_y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
finish = True
block_on_right = False
block_on_left = False
is_player_on_block = False
for block in blocks:
block.load_image(background_x)
for i in range(int(player.vy * DELTA_T+0.5)):
for x in player.get_x_list():
if player.get_bottom_y() + i == block.get_y() and x in block.get_x_list():
is_player_on_block = True
player.y += i
if player.y - i == block.get_bottom_y() and x in block.get_x_list():
player.set_jump(False)
player.y -= i
for i in range(int(player.vx*DELTA_T+0.5)):
for y in player.get_y_list():
if player.get_right_x() + i == block.get_x() and y in block.get_y_list():
block_on_right = True
player.x += (i - 1)
background_x -= (i - 1)
if player.x - i == block.get_right_x() and y in block.get_y_list():
block_on_left = True
player.x -= (i - 1)
background_x += (i - 1)
for monster in monsters:
if monster.is_crash(block):
monster.turn_around()
keys = pygame.key.get_pressed()
if (keys[pygame.K_UP] or keys[pygame.K_SPACE] or keys[pygame.K_w]) and not player.is_mid_air():
player.set_jump(True)
if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) and not block_on_right:
if player.get_direction() != "right":
player.turn_around()
player.set_walk(True)
background_x -= player.vx * DELTA_T
player.x += player.vx * DELTA_T
if (keys[pygame.K_LEFT] or keys[pygame.K_a]) and not block_on_left:
if player.get_direction() != "left":
player.turn_around()
player.set_walk(True)
if background_x != 0:
background_x += player.vx * DELTA_T
player.x -= player.vx * DELTA_T
if not any(keys):
player.stand_still()
player.set_walk(False)
for monster in monsters:
monster.step()
SCREEN.blit(monster.get_image(), (background_x + monster.get_x(), monster.get_y()))
is_player_on_ground = False
for i in range(int(player.vy * DELTA_T+0.5)):
if player.get_bottom_y() + i == FLOOR_Y:
player.y += i
is_player_on_ground = True
if is_player_on_block or is_player_on_ground:
player.set_is_mid_air(False)
else:
player.set_is_mid_air(True)
player.fall()
player.check_jump()
player_image = player.get_image().convert()
player_image.set_colorkey(PINK)
SCREEN.blit(player_image, (player.x + background_x, player.y))
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()