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.
Related
I'm trying to make a very simple game with my son in Kivy and Python. We are trying to make our viewport (camera) centered over the player as they move around our map that is self generating. We get the initial view, then as the player moves the initial chunk is shown in the correct place, but new chunks aren't being drawn at all.
By debugging, we can tell that we are creating chunks, that they have good values, and that our draw_chunks function knows to grab more chunks and to draw them. They just aren't being drawn. We think that our code for drawing the rectangles is probably wrong, but works for the initial load of the game. We've spent a couple hours trying to fix it. We've adjusted the viewport position a couple different ways as well as the rectangle code, but nothing seems to work. I'm hoping someone can point out what we missed. It is probably something very obvious or silly that we are overlooking. Does anyone have any ideas?
import kivy
import random
from kivy.app import App
from kivy.clock import Clock
from kivy.graphics import Color, Ellipse, Line, Rectangle
from kivy.uix.widget import Widget
from kivy.config import Config
from kivy.core.window import Window
import enum
# Constants for the chunk size and map dimensions
CHUNK_SIZE = 48
TILE_SIZE = 16
MAP_WIDTH = 256
MAP_HEIGHT = 256
#**************************
#* Tile Int Enum Class *
#**************************
class TileEnum(enum.IntEnum):
GRASS = 0
DIRT = 1
CONCRETE = 2
ASPHALT = 3
# Class to represent the game map
class GameMap:
def __init__(self, seed=None):
self.seed = seed
if seed is not None:
random.seed(seed)
self.chunks = {}
self.first_chunk = False
def generate_chunk(self, chunk_x, chunk_y):
# Use the RNG to generate the terrain for this chunk
terrain = []
for x in range(0, CHUNK_SIZE):
column = []
for y in range(0, CHUNK_SIZE):
column.append(random.randint(0, 3))
terrain.append(column)
return terrain
def get_chunk(self, chunk_x, chunk_y):
# Check if the chunk has already been generated
if (chunk_x, chunk_y) in self.chunks:
print("found it",chunk_x, chunk_y)
return self.chunks[(chunk_x, chunk_y)]
else:
# Generate the chunk and store it in the chunk cache
chunk = self.generate_chunk(chunk_x, chunk_y)
self.chunks[(chunk_x, chunk_y)] = chunk
print("made it",chunk_x,chunk_y)
return chunk
# Class to represent the player
class Player:
def __init__(self, pos=(0, 0)):
self.x, self.y = pos
self.speed = TILE_SIZE/2
def move_left(self):
self.x += self.speed
def move_right(self):
self.x -= self.speed
def move_up(self):
self.y -= self.speed
def move_down(self):
self.y += self.speed
class GameScreen(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.viewport_size = (TILE_SIZE*CHUNK_SIZE, TILE_SIZE*CHUNK_SIZE)
self.viewport_pos = (0, 0)
self.size = self.viewport_size
self.map = GameMap(seed=123)
self.player = Player((self.viewport_size[0]/2, self.viewport_size[1]/2))
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_keyboard_down)
def draw_chunks(self):
# Determine the chunks that are currently in view
viewport_left = int(self.viewport_pos[0] // (CHUNK_SIZE * TILE_SIZE))
viewport_top = int(self.viewport_pos[1] // (CHUNK_SIZE * TILE_SIZE))
viewport_right = int((self.viewport_pos[0] + self.viewport_size[0]) // (CHUNK_SIZE * TILE_SIZE))
viewport_bottom = int((self.viewport_pos[1] + self.viewport_size[1]) // (CHUNK_SIZE * TILE_SIZE))
print(viewport_left, viewport_top, viewport_right, viewport_bottom)
# Iterate over the visible chunks and draw them
for x in range(viewport_left, viewport_right + 1):
for y in range(viewport_top, viewport_bottom + 1):
chunk = self.map.get_chunk(x, y)
#print(chunk)
for i in range(len(chunk)):
for j in range(len(chunk[i])):
if chunk[i][j] == TileEnum.GRASS:
# Draw a green square for grass
with self.canvas:
Color(0.25, 0.75, 0.25)
elif chunk[i][j] == TileEnum.DIRT:
# Draw a brown square for dirt
with self.canvas:
Color(0.75, 0.5, 0.25)
elif chunk[i][j] == TileEnum.CONCRETE:
# Draw a gray square for concrete
with self.canvas:
Color(0.5, 0.5, 0.75)
elif chunk[i][j] == TileEnum.ASPHALT:
# Draw a black square for asphalt
with self.canvas:
Color(0.25, 0.25, 0.5)
with self.canvas:
Rectangle(pos=(
(x * CHUNK_SIZE + i) * TILE_SIZE + self.viewport_pos[0],
(y * CHUNK_SIZE + j) * TILE_SIZE + self.viewport_pos[1]),
size=(TILE_SIZE, TILE_SIZE))
def draw_player(self):
# Draw a circle for the player
with self.canvas:
Color(0, 0.5, 0)
Ellipse(pos=(self.viewport_size[0]/2 - (TILE_SIZE/2), self.viewport_size[0]/2 - (TILE_SIZE/2)), size=(TILE_SIZE, TILE_SIZE))
def update(self, dt):
# Update the viewport position to keep the player centered
self.viewport_pos = (self.player.x - self.viewport_size[0]/2, self.player.y - self.viewport_size[1]/2)
print(self.viewport_pos)
# Redraw the chunks and player
self.canvas.clear()
self.draw_chunks()
self.draw_player()
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
#print(keycode)
if keycode[1] == 'left':
self.player.move_left()
elif keycode[1] == 'right':
self.player.move_right()
elif keycode[1] == 'up':
self.player.move_up()
elif keycode[1] == 'down':
self.player.move_down()
# Main application class
class ProceduralGenerationGameApp(App):
def build(self):
self.title = "Procedural Generation Game"
Config.set("graphics", "width", "768")
Config.set("graphics", "height", "768")
Config.set("graphics", "resizable", False)
Config.set("graphics", "borderless", False)
Config.set("graphics", "fullscreen", False)
Config.set("graphics", "window_state", "normal")
Config.set("graphics", "show_cursor", True)
Config.write()
window_width = Config.getint("graphics", "width")
window_height = Config.getint("graphics", "height")
# Create the game screen and schedule the update function to be called every frame
game_screen = GameScreen()
Window.size = (window_width, window_height)
Clock.schedule_interval(game_screen.update, 1)# 1.0 / 60.0)
return game_screen
if __name__ == "__main__":
ProceduralGenerationGameApp().run()
We updated the Rectangle code to this and reversed the players direction in his move functions:
with self.canvas:
x_chunk_offset = (x * CHUNK_SIZE * TILE_SIZE)
y_chunk_offset = (y * CHUNK_SIZE * TILE_SIZE)
x_tile_offset = (i * TILE_SIZE)
y_tile_offset = (j * TILE_SIZE)
actual_x = x_chunk_offset + x_tile_offset - self.viewport_pos[0]
actual_y = y_chunk_offset + y_tile_offset - self.viewport_pos[1]
Rectangle(pos=(actual_x, actual_y), size=(TILE_SIZE, TILE_SIZE))
I m using :
Windows 10
Python 3.9
Kivy 2.0.0
and my question is : I just follow the tutorial in the kivy documentation. I want to display a label duce = StringProperty() for a short period of time like after 1 s and it will disappear automatically. I can make it work but want to ask / search for some better solutions / more appropriate solutions :) thx
Code:
main.py
#import kivy
#kivyVersion = kivy.__version__ # check kivy version
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, StringProperty, ReferenceListProperty, ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
class Ball(Widget):
Vx = NumericProperty(0)
Vy = NumericProperty(0)
Velocity = ReferenceListProperty(Vx, Vy)
def Move(self):
self.pos = Vector(*self.Velocity) + self.pos
class Paddle(Widget):
score = StringProperty("0") # set the score label be a string with text "0" at the beginning
def Bounce(self, ball): # to make the ball move faster when touched the paddle --> increase difficulty + more fun
if self.collide_widget(ball):
Vx, Vy = ball.Velocity # unpack the ball.Velocity
offset = (ball.center_y - self.center_y) / (self.height / 2)
bounce = Vector((Vx * -1), Vy)
bouncedVelocity = bounce * 1.1
ball.Velocity = bouncedVelocity.x, bouncedVelocity.y + offset
class PongGame(Widget): # ref. mypong.kv
ball = ObjectProperty(None)
leftplayer = ObjectProperty(None)
rightplayer = ObjectProperty(None)
duce = StringProperty()
GameOver = False
def ServeBall(self, v = (4, 0)):
self.ball.center = self.center # set the cennter of the ball at the center of the screen
self.ball.Velocity = v # start velocity # Vector(4, 0).rotate(randint(0, 360)) # randomly set a Vx and Vy for the ball
def Update(self, dt):
self.ball.Move() # Move() function to make the ball move with constant v
self.leftplayer.Bounce(self.ball) # bounce when touched player paddle
self.rightplayer.Bounce(self.ball)
if self.ball.y < self.y or self.ball.top > self.top: # touch bottom or top --> bounce
self.ball.Vy *= -1 # same velocity by in opposite direction
if self.ball.x < self.x: # touch left --> bounce and rightplayer score += 1
self.rightplayer.score = str(int(self.rightplayer.score) + 1)
self.ball.Vx *= -1
self.ServeBall(v = (4, 0))
if self.ball.right > self.width: # touch right --> bounce and leftplayer score += 1
self.leftplayer.score = str(int(self.leftplayer.score) + 1)
self.ball.Vx *= -1
self.ServeBall(v = (-4, 0))
def Duce(): # duce | when one of the player score 10 . two player 's score different == 1
self.GameOver = False
Duce()
######################
if int(self.leftplayer.score) == 11 or int(self.rightplayer.score) == 11: # display the text "Duse !" for a short while
self.duce = "Duce !"
else:
self.duce = ""
######################
if self.leftplayer.score != self.rightplayer.score:
try:
if int(self.leftplayer.score) >= 10 or int(self.rightplayer.score) >= 10:
if int(self.leftplayer.score) - int(self.rightplayer.score) == 1 or int(self.leftplayer.score) - int(self.rightplayer.score) == -1:
Duce()
elif int(self.leftplayer.score) - int(self.rightplayer.score) >= 2 or int(self.leftplayer.score) - int(self.rightplayer.score) <= -2:
self.GameOver = True
self.ServeBall(v = (0, 0)) # set the ball rest on the screen 's center
if int(self.leftplayer.score) > int(self.rightplayer.score):
self.leftplayer.score = "You Win !" # change label text
self.rightplayer.score = "Ops !" # change label text
elif int(self.rightplayer.score) > int(self.leftplayer.score):
self.leftplayer.score = "Ops !" # change label text
self.rightplayer.score = "You Win !" # change label text
except RecursionError:
pass
except ValueError:
pass
except Exception as e:
print("Error :", e)
def on_touch_move(self, touch): # | widget | function name must be on_touch_move
if touch.x < (self.width / 3):
self.leftplayer.center_y = touch.y
if touch.x > (self.width - self.width / 3):
self.rightplayer.center_y = touch.y
if self.GameOver == True: # when game is over
if touch.x < self.width: # when player toch screen --> reset all
self.GameOver = False
self.leftplayer.score = "0"
self.rightplayer.score = "0"
self.ServeBall()
class MyPongApp(App): # name of the .kv file should be the text before "App" # start up
def build(self):
self.title = "My First Kivy Game"
Game = PongGame() # creating obj for class PongGame
Game.ServeBall() # run class PongGame function ServeBall()
Clock.schedule_interval(Game.Update, 1.0/120.0)
return Game
if __name__ == "__main__":
MyPongApp().run()
mypong.kv :
#:kivy 2.0.0
<Ball>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<Paddle>:
size: 25, 200
canvas:
Rectangle:
pos: self.pos
size: self.size
<PongGame>:
ball: myBall
leftplayer: left_player
rightplayer: right_player
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: root.leftplayer.score
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: root.rightplayer.score
Label:
font_size: 70
center_x: root.width /2
center_y: root.top / 2
text: root.duce
Ball:
id: myBall
center: self.parent.center
Paddle:
id: left_player
x: root.x
center_y: root.center_y
Paddle:
id: right_player
x: root.width - self.width
center_y: root.center_y
my own solution :
#####################
if int(self.leftplayer.score) == 11 or int(self.rightplayer.score) == 11: # display the text "Duse !" for a short while
self.duce = "Duce !"
else:
self.duce = ""
######################
and do I also need to add sth on the mypong.kv ?
You can use Clock object refer to documentation here
This is how you hide the label after 1 second this will hide it not just remove the text. However you should give your label an id to be able to modify the property.
from kivy.clock import Clock
def hide_label(dt):
self.ids.[labelId].size_hint_x=0 # to hide the label best practice is setting size to 0
self.ids.[labelId].size_hint_x=1 # Show the label
Clock.schedule_once(hide_label, 1)
I'm new to python and am working on a final project for my intro to python class. I have the majority of a paddle ball game done, but can't figure out how to make the ball object bounce off of my paddle object.
I've looked on Stackoverflow for a while and have spent a few hours trying to figure it out on my own without any success. If anyone has any ideas I could really use the help.
If there's anything I need to explain better in order for you to get a better understanding please just comment.
GUI FILE:
Import tkinter, random, particle, and helpers
from tkinter import *
from ball import *
from paddle import *
from time import *
class PaddleBall:
def __init__(self, window):
''' Construct the paddle ball GUI '''
self.window = window
self.window.protocol('WM_DELETE_WINDOW', self.safe_exit)
self.width = 700
self.height = 900
self.canvas = Canvas(self.window, bg='black', width=self.width, height=self.height, highlightthickness=0)
self.canvas.bind_all("<KeyPress-Left>", self.move_left)
self.canvas.bind_all("<KeyPress-Right>", self.move_right)
self.canvas.pack()
# Create a label to indicate instructions
instructions = Label(window, text="Controls: Left & Right Arrow Keys")
instructions.pack(side=BOTTOM, expand=YES)
# Create a button to clear Ball
restart_button = Button(window, text="Play", command=self.reset)
restart_button.pack(side=BOTTOM, expand=YES)
self.ball = Ball(350, 350)
self.paddle = Paddle(300, 850, 400, 860, 0, 0)
self.terminated = False
self.render()
def ballobject(self):
self.ball = Ball(350, 350)
self.paddle = Paddle(300, 850, 400, 860, 0, 0)
self.render()
def reset(self):
self.terminated = True
def safe_exit(self):
''' Turn off the event loop before closing the GUI '''
self.terminated = True
self.window.destroy()
# Render everything
def render(self):
# While program is not terminated
if not self.terminated:
# Erase Canvas
self.canvas.delete(ALL)
# Move ball
self.ball.move(self.canvas, self.paddle)
# Render ball
self.ball.render(self.canvas)
# Render paddle
self.paddle.render(self.canvas)
# use distance() to detect collision between ball and paddle.
'''Ball.bounce(self)'''
# Animate the particles movement
self.canvas.after(10, self.render)
else:
# Erase Canvas
self.canvas.delete(ALL)
self.terminated = False
self.canvas.after(50, self.ballobject)
def move_left(self, event):
self.paddle.move_left(event)
def move_right(self, event):
self.paddle.move_right(event)
if __name__ == '__main__':
root = Tk()
root.option_add('*font', ('Verdana', 12, 'bold italic')) # Found at http://effbot.org/tkinterbook/tkinter-widget-styling.htm
root.resizable(0,0) # Found at https://mail.python.org/pipermail/tutor/2001-September/008504.html
root.title('Paddle Ball')
root.wm_attributes("-topmost", -1)
app = PaddleBall(root)
root.mainloop()
BALL CLASS FILE:
class Ball:
'''
Ball models a single ball that may be rendered to a canvas
'''
def __init__(self, x, y, radius = 15,):
'''
Constructor
'''
self._x = x
self._y = y
self._velX = randint(-10,10)
self._velY = randint(-10,-5)
self._radius = radius
self._color = 'white'
self._tx = 350
self._ty = 400
self._t = ""
self._tfill = "red"
self._tfont = ("Arial", 35, "bold italic")
# This method renders the ball
def render(self, canvas):
canvas.create_oval(self._x - self._radius, self._y - self._radius, self._x + self._radius, self._y + self._radius, fill = self._color)
canvas.create_text(self._tx, self._ty, text = self._t, fill = self._tfill, font = self._tfont)
# This method moves the ball
def move(self, canvas, Paddle):
# Update Position
self._x += self._velX
self._y += self._velY
# If the ball hits any of the wall negate the velocity
if (self._x + self._radius > canvas.winfo_reqwidth() and self._velX > 0) or (self._x - self._radius < 0 and self._velX < 0):
self._velX = -self._velX
if (self._y + self._radius < 0 and self._velY < 0):
self._velY = -self._velY
if (self._y + self._radius > canvas.winfo_reqheight() and self._velY > 0):
self._velY = 0
self._velX = 0
self._t = " GAME OVER! \n Click the play button to play again."
#*****THIS IS WHAT I'M HAVING TROUBLE WITH******
# Determine if the ball hits the paddle
if ((self._x + self._radius > Paddle._x(self) and self._velX > 0) or (self._x + self._radius < Paddle._x2(self))) and (self._y < Paddle._y(self)):
self._velX = -self._velX
PADDLE CLASS FILE:
# Import math and helpers
from tkinter import *
import math
from gui import *
class Paddle:
def __init__(self, x, y, x2, y2, velX, velY):
'''
Constructor
'''
self._x = x
self._y = y
self._x2 = x2
self._y2 = y2
self._velX = velX
self._velY = velY
self._color = 'white'
def getpadx(self):
return self._x
def getpady(self):
return self._y
def getpadx1(self):
return self._x2
def getpady2(self):
return self._y2
# This method renders the paddle
def render(self, canvas):
canvas.create_rectangle(self._x, self._y, self._x2, self._y2, fill = self._color)
# This method moves the paddle
def move(self, canvas):
# Update Position
# If the paddle hits any of the wall negate the velocity
if (self._x + self._radius > canvas.winfo_reqwidth() and self._velX > 0) or (self._x - self._radius < 0 and self._velX < 0):
self._velX = -self._velX
def move_left(self, event):
self._x -= 35
self._x2 -= 35
def move_right(self, event):
self._x += 35
self._x2 += 35
I figured it out with the help of a friend. All I had to do was change this code:
From this:
if ((self._x + self._radius > Paddle._x(self) and self._velX > 0) or (self._x + self._radius < Paddle._x2(self))) and (self._y < Paddle._y(self)):
self._velX = -self._velX
To this:
`if (self._x > Paddle._x) and (self._x < Paddle._x2):
if (self._y + self._radius > Paddle._y):
self._velY = -self._velY
self._velX = self._velX + randint(-2,2)`
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)
Below is a little Kivy app I'm working on. At the moment I'm trying to implement a basic reset feature. Essentially, if an enemy collides with the player, I want the game to reset. The games ends by unscheduling clock schedules upon collision with an enemy. I thought after that I could just call main() afterwards, but this simply causes massive bugs where the game spews out errors left and right. I also tried added reschedule rules after the unshedule, but that just overrides the unshedule and keeps the game going.
Note: The game is handled mostly from the game class towards the bottom of the code. It's also where you'll see clock schedule rules.
import kivy
kivy.require('1.1.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty, NumericProperty, BooleanProperty
from kivy.core.audio import SoundLoader
import math
from kivy.clock import Clock
import time
import random
sound = SoundLoader.load('gamemusic.mp3')
PLAYER_SPEED = 10
ENEMY_SPEED = 4
ENEMY_SPAWN = 5
UPDATE_SPEED = .01
MIN_INITIAL_PLAYER_MINION_DISTANCE = 200
UPDATE_SCORE = 1
MAX_DECOYS = 3
if sound:
sound.loop = True
sound.play()
class Movable(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
target = ListProperty([])
def move(self):
dx = self.target[0] - self.center[0]
dy = self.target[1] - self.center[1]
total = math.sqrt(dx**2 + dy**2)
if total >0:
self.velocity_x = (dx / total) * self.speed
self.velocity_y = (dy / total) * self.speed
new_x = self.pos[0] + self.velocity_x
new_y = self.pos[1] + self.velocity_y
self.pos = (new_x,new_y)
elif total <=0:
self.velocity_x = self.speed
self.velocity_y = self.speed
class Player(Movable):
target = ListProperty([])
def __init__(self, **kwargs):
Widget.__init__(self)
self.target = [399.0, 399.0]
self.speed = PLAYER_SPEED
def update_target(self, new_target):
self.target = new_target
if distance_pos(self.center, self.target) <= self.speed*2:
self.target = self.center
class Enemy(Movable):
randomColor = random.randint(0,1)
alert = BooleanProperty(False)
def __init__(self, **kwargs):
Widget.__init__(self)
self.world_width = kwargs.get("width",0)
self.world_height = kwargs.get("height",0)
self.speed = ENEMY_SPEED
x_target = float(random.randint(0, self.world_width))
y_target = float(random.randint(0, self.world_height))
self.target = [x_target, y_target]
self.center = kwargs.get('start_pos',[0,0])
def update_target(self, player, enemies, decoys):
alert_list = self.alerted(enemies)
if len(decoys) > 0:
self.target = decoys[0].center
self.alert = False
elif distance(player, self) < 150:
self.alert = True
self.target = player.center
elif len(alert_list) > 0:
self.target = alert_list[0].center
self.alert = False
else:
if distance_pos(self.center, self.target) <= self.speed*2:
x_new_target = float(random.randint(0, self.world_width))
y_new_target = float(random.randint(0, self.world_height))
self.target = [x_new_target, y_new_target]
def alerted(self, enemies):
alert_list = []
for item in enemies:
if item.alert == True and distance(self, item) < 150:
alert_list.append(item)
return alert_list
class Decoy(Widget):
def __init__(self, **kwargs):
Widget.__init__(self)
self.center = kwargs.get('start_pos',[0,0])
pass
def distance(widget1, widget2):
return distance_pos(widget1.center, widget2.center)
def distance_pos(pos1, pos2):
dist = math.sqrt((pos1[0]-pos2[0])**2 + (pos1[1]-pos2[1])**2)
return dist
class Game(Widget):
player1 = ObjectProperty(None)
enemies = ListProperty([])
decoys = ListProperty([])
score = NumericProperty()
def setup(self):
self.enemies = []
self.decoys = []
self.player1.center = self.center
self.setup_schedules()
#Don't forget about good code organization!
def setup_schedules(self):
Clock.schedule_interval(self.update, UPDATE_SPEED)
Clock.schedule_interval(self.spawn_enemy, ENEMY_SPAWN)
Clock.schedule_interval(self.increase_score, UPDATE_SCORE)
def update(self,dt):
self.player1.move()
for item in self.enemies:
item.update_target(self.player1,self.enemies,self.decoys)
item.move()
if self.player1.collide_widget(item):
Clock.unschedule(self.spawn_enemy)
Clock.unschedule(self.update)
Clock.unschedule(self.increase_score)
"""
def death_restart(self, player):
if self.collide_widget(player):
print("error")
#main()
"""
def spawn_enemy(self, dt):
if len(self.enemies) <= 8:
x = float(random.randint(0, self.width))
y = float(random.randint(0, self.height))
enemy = Enemy(start_pos = (x,y),width = self.width,height = self.height)
while distance(enemy, self.player1)< MIN_INITIAL_PLAYER_MINION_DISTANCE:
x = float(random.randint(0, self.width))
y = float(random.randint(0, self.height))
enemy.pos = (x,y)
self.enemies.append(enemy)
self.add_widget(enemy)
def spawn_decoy(self, location):
x = location[0]
y = location[1]
decoy = Decoy(start_pos = (x,y))
self.decoys.append(decoy)
self.add_widget(decoy)
def increase_score(self, dt):
self.score += 1
#on_touch_move vs on_touch_down
def on_touch_move(self, touch):
self.player1.update_target([touch.x, touch.y])
def on_touch_down(self, touch):
if touch.is_double_tap and len(self.decoys) < MAX_DECOYS:
self.spawn_decoy([touch.x, touch.y])
#Entry Point into app
class GameApp(App):
def build(self):
game = Game()
game.setup()
return game
def main():
GameApp().run()
main()