Related
I'm making a racing game with almost realistic cars in pygame. I made the cars succesfully and I want to make the car move a bit slower when it's not touching the drive way. For that, I want to use pygame mask collisions. Instead of using bounding boxes, they use the pixels and count them based on how many pixels of something are touching something. This all works correctly, but when I use the cam.follow_object(player) function, the game behaves very weird and there rise unexplainable bugs.
I included each individual picture and file, but I also included it on github because I know it's annoying to download all images one by one.
Assets:
app.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera, level
pygame.init()
# Basic Variables
screen_width = 1000
screen_height = 1000
fps = 80
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
cam = camera.Camera(1, screen)
#cam.zoom_game(1.55)
stage = "home"
# Loading Images
images = {
"start" : pygame.transform.scale(pygame.image.load("road_texture.png"), (200*cam.zoom, 75*cam.zoom)).convert_alpha(),
"bcg" : pygame.transform.scale(pygame.image.load("map2.png"), (1000*cam.zoom, 1000*cam.zoom)).convert_alpha(),
"car" : pygame.image.load("car.png").convert_alpha()
}
# Functionality Functions
def play_game():
global stage
stage = "game"
# Instansiating stuff
pos_save = pySave.Save_Manager("saved_info", "pos")
start_button = ui.Button((500,500), images["start"], play_game, text="startgame")
test_car = car.CarSprite(images["car"], 400, 400,[2, 2.3, 2.7], 0.013, rotations=360, camera=cam)
speedometer = ui.TextWithBackground((100,50), (100,950), images["start"])
level = level.Level(images["bcg"], test_car,cam,pos = (0,100))
# Groups and Lists
car_group = pygame.sprite.Group()
# Adding to Groups
car_group.add(test_car)
# Game Functions
def render():
if stage == "home":
screen.fill((255,255,255))
start_button.update("Start Game")
start_button.draw(screen)
elif stage == "game":
screen.fill((0,76,18))
level.update(screen)#screen.blit(images["bcg"], (0-cam.scroll[0],0-cam.scroll[1]))
car_group.update()
car_group.draw(screen)
#cam.follow(test_car, )
#speedometer.draw(screen)
speedometer.update(screen,round(test_car.speed*60,2) , " km/h")
#cam.zoom_game(2)
def collisions():
pass
run = True
while run:
clock.tick_busy_loop(80)
render()
collisions()
for event in pygame.event.get():
if event.type == QUIT:
run = False
pos_save.save("x", test_car.rect.x)
pos_save.save("y", test_car.rect.y)
pos_save.apply()
print(f"Quit with {round(clock.get_fps(), 2)} FPS")
quit()
pygame.display.update()
car.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, camera
class CarSprite( pygame.sprite.Sprite ):
def __init__( self, car_image, x, y, max_speed, accel,rot_speed=[1.8, 2.2, 3] ,rotations=360, camera="" ):
pygame.sprite.Sprite.__init__(self)
self.rotated_images = {}
self.min_angle = ( 360 / rotations )
for i in range( rotations ):
rotated_image = pygame.transform.rotozoom( pygame.transform.scale(car_image, (12*camera.zoom,25*camera.zoom)), 360-90-( i*self.min_angle ), 1 )
self.rotated_images[i*self.min_angle] = rotated_image
self.min_angle = math.radians( self.min_angle )
self.image = self.rotated_images[0]
self.rect = self.image.get_rect()
self.rect.center = ( x, y )
self.reversing = False
self.heading = 0
self.speed = 0
self.velocity = pygame.math.Vector2( 0, 0 )
self.position = pygame.math.Vector2( x, y )
self.speed_hardening = 1
self.acc = False
self.steer_strenght_acc = rot_speed[0]
self.steer_strength_normal= rot_speed[1]
self.steer_strength_drift= rot_speed[2]
self.steer_strength = rot_speed[1]
self.drift_point = 0.00
self.accel = accel
self.max_speed = self.accel * 150
self.cam = camera
def turn( self, ori=1 ):
if self.speed > 0.1 or self.speed < 0. :
self.heading += math.radians( self.steer_strenght * ori )
image_index = int((self.heading + self.min_angle / 2) / self.min_angle) % len(self.rotated_images)
image = self.rotated_images[image_index]
if self.image is not image:
x,y = self.rect.center
self.image = image
self.rect = self.image.get_rect()
self.rect.center = (x,y)
def accelerate( self):
self.speed += self.accel
def brake( self ):
if self.speed > 0:
self.speed -= self.accel * 3
if abs(self.speed) < 0.1:
self.speed = 0
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
def move(self):
keys = pygame.key.get_pressed()
if keys[K_w]:
self.accelerate()
if keys[K_s]:
self.brake()
if keys[K_a]:
self.turn(-1)
if keys[K_d]:
self.turn()
if keys[pygame.K_s] or keys [pygame.K_w]:
self.acc = True
else:
self.acc = False
def update( self ):
self.move()
self.speed_hardening = self.speed / 100
self.speed = round(self.speed, 3)
if self.acc:
self.steer_strenght = self.steer_strenght_acc
else:
self.steer_strenght = self.steer_strength_normal
if self.speed > self.max_speed and not pygame.key.get_pressed()[K_SPACE] and not self.drift_point > 0:
self.speed += self.accel / 4 - self.speed_hardening / 2
if self.speed > self.max_speed * 1.8:
self.speed = self.max_speed * 1.8
if self.speed < -self.max_speed / 4:
self.speed = -self.max_speed / 4
if not pygame.key.get_pressed()[K_SPACE]:
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
self.speed += self.drift_point
self.drift_point -= 0.0001
if self.drift_point < 0:
self.drift_point = 0
self.speed -= self.drift_point
else:
self.steer_strenght = self.steer_strength_drift
self.drift_point += 0.0001
if self.drift_point > self.accel / 1.5:
self.drift_point = self.accel / 1.5
if not self.acc and not self.speed < 0.04:
self.speed -= (self.accel / 2) + self.speed_hardening
if self.speed < 0.05:
self.speed = 0
self.position += self.velocity
self.rect.center = self.position
level.py
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera
screen_width = 1000
screen_height = 1000
class Level:
def __init__(self,image, car, camera, pos=(0,0)):
# Convert the images to a more suitable format for faster blitting
self.image = image.convert()
self.road = image
self.cam = camera
self.x,self.y = pos
self.bcg_mask = pygame.mask.from_surface(self.road)
self.car = car
self.get_car_mask()
def update(self, screen):
# Calculate the overlap between the car mask and the background mask
overlap = self.bcg_mask.overlap_mask(
self.car_mask,
(self.car.rect.x, self.car.rect.y)
)
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())
def get_car_mask(self):
# Convert the car image to a more suitable format for faster blitting
carimg = self.car.image.convert()
carimg.set_colorkey((0,0,0))
self.carimg = carimg
self.car_mask = pygame.mask.from_surface(self.carimg)
camera.py:
import pygame
from pygame.locals import *
class Camera:
def __init__(self, speed, screen):
self.scroll = [5,5]
self.speed = speed
self.screen = screen
self.zoom = 1
def move_on_command(self):
keys = pygame.key.get_pressed()
if keys[K_UP]:
self.scroll[1] -= self.speed
if keys[K_DOWN]:
self.scroll[1] += self.speed
if keys[K_RIGHT]:
self.scroll[0] += self.speed
if keys[K_LEFT]:
self.scroll[0] -= self.speed
def follow(self, obj, speed=12):
#self.scroll[0], self.scroll[1] = obj.rect.x, obj.rect.y
if (obj.rect.x - self.scroll[0]) != self.screen.get_width()/2:
self.scroll[0] += ((obj.rect.x - (self.scroll[0] + self.screen.get_width()/2)))
if obj.rect.y - self.scroll[1] != self.screen.get_height()/2:
self.scroll[1] += ((obj.rect.y - (self.scroll[1] + self.screen.get_height()/2)))
def zoom_game(self, zoom):
self.zoom = zoom
ui.py :
import pygame
from pygame.locals import *
from random import choice, randint
class Button:
def __init__(self, pos, image, action, click_times=1, dissapear=True , text="", textcolor = (255,255,255), fontsize=30):
self.image = image
self.rect = self.image.get_rect(center=pos)
self.clicked = False
self.clicked_times = click_times
self.dissapear = dissapear
self.dont_draw = False
self.function = action
self.text= text
if not self.text == "":
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = textcolor
def update(self, var=""):
pressed = pygame.mouse.get_pressed()[0]
if not self.text == "":
self.text = self.font.render(f"{var}", 1, self.color)
self.text_rect = self.text.get_rect(center = self.rect.center)
if pressed and not self.clicked and not self.clicked_times <= 0 and self.rect.collidepoint(pygame.mouse.get_pos()):
self.function()
self.clicked = True
self.clicked_times -= 1
if not pressed:
self.clicked = False
if self.clicked_times <= 0:
if self.dissapear:
self.dont_draw = True
def draw(self, screen):
if not self.dont_draw:
screen.blit(self.image, (self.rect.x,self.rect.y))
screen.blit(self.text, (self.text_rect.x, self.text_rect.y))
class TextWithBackground:
def __init__(self, image_size, pos, image, fontsize=30, colour=(255, 255, 255)):
self.image = pygame.transform.scale(image, image_size)
self.rect = self.image.get_rect(center=pos)
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = colour
self.text = self.font.render("", 1, self.color)
self.other_text = None
self.other_rect = None
def update(self, screen, variable, othertext=""):
self.text = self.font.render(f"{variable}", 1, self.color)
self.text_rect = self.text.get_rect(center=self.rect.center)
if othertext:
self.other_text = self.font.render(f"{othertext}", 1, self.color)
self.other_rect = self.other_text.get_rect(topleft=(self.rect.center[0] - 5, self.rect.center[1] - 5))
screen.blit(self.image, (self.rect.x, self.rect.y))
screen.blit(self.text, (self.rect.x, self.rect.y))
if self.other_text:
screen.blit(self.other_text, (self.other_rect.x, self.other_rect.y))
Here you can find my images:
car.png
map2.png
road_texture.png
See PyGame collision with masks. You need to calculate the offset between the car and the map when you get the overlap area of the masks:
class Level:
# [...]
def update(self, screen):
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Calculate the overlap between the car mask and the background mask
offset = (self.car.rect.x - self.x, self.car.rect.y - self.y)
overlap = self.bcg_mask.overlap_mask(self.car_mask, offset)
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())
I am having problems with the below code. Can someone please help and explain why the game over/reset and score functions are not working?
Below is the code im working from. My problem is that i can not see why the game over/reset function does not work. When the ship crashes into an astroid the game abruptly ends, but when it crashes into the barrier it does not indicate game over. Also, it's not reflecting the score.
Sorry if i wasn't clear before.
Here starts the code:
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.properties import NumericProperty, ObjectProperty
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.graphics import Rectangle, Color, Canvas
from kivy.config import Config
from functools import partial
from random import *
Config.set('graphics', 'resizable', 0)
Window.clearcolor = (0, 0, 0, 1.)
class MyButton(Button):
#class used to get uniform button styles
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.font_size = Window.width*0.018
class SmartMenu(Widget):
#the instance created by this class will appear
#when the game is started for the first time
buttonList = []
def __init__(self, **kwargs):
#create custom events first
self.register_event_type('on_button_release')
super(SmartMenu, self).__init__(**kwargs)
self.layout = BoxLayout(orientation = 'vertical')
self.layout.width = Window.width/2
self.layout.height = Window.height/2
self.layout.x = Window.width/2 - self.layout.width/2
self.layout.y = Window.height/2 - self.layout.height/2
self.add_widget(self.layout)
def on_button_release(self, *args):
#print 'The on_button_release event was just dispatched', args
#don't need to do anything here. needed for dispatch
pass
def callback(self,instance):
#print('The button %s is being pressed' % instance.text)
self.buttonText = instance.text
self.dispatch('on_button_release') #dispatching the callback event 'on_button_release' to tell teh parent instance to read the button text
def addButtons(self):
for k in self.buttonList:
tmpBtn = MyButton(text = k)
tmpBtn.background_color = [.4, .4, .4, .4]
tmpBtn.bind(on_release = self.callback) #when the button is released the callback function is called
self.layout.add_widget(tmpBtn)
def buildUp(self):
#self.colorWindow()
self.addButtons()
class SmartStartMenu(SmartMenu):
#setup the menu button names
buttonList = ['start', 'about']
def __init__(self, **kwargs):
super(SmartStartMenu, self).__init__(**kwargs)
self.layout = BoxLayout(orientation = 'vertical')
self.layout.width = Window.width/2
self.layout.height = Window.height/2
self.layout.x = Window.width/2 - self.layout.width/2
self.layout.y = Window.height/2 - self.layout.height/2
self.add_widget(self.layout)
self.msg = Label(text = 'Flappy Ship')
self.msg.font_size = Window.width*0.07
self.msg.pos = (Window.width*0.45,Window.height*0.75)
self.add_widget(self.msg)
self.img = Image(source = 'lens2.png')
self.img.size = (Window.width*1.5,Window.height*1.5)
self.img.pos = (-Window.width*0.2,-Window.height*0.2)
self.img.opacity = 0.35
self.add_widget(self.img)
class WidgetDrawer(Widget):
#This widget is used to draw all of the objects on the screen
#it handles the following:
# widget movement, size, positioning
def __init__(self, imageStr, **kwargs):
super(WidgetDrawer, self).__init__(**kwargs)
with self.canvas:
self.size = (Window.width*.002*25,Window.width*.002*25)
self.rect_bg=Rectangle(source=imageStr, pos=self.pos, size=self.size)
self.bind(pos=self.update_graphics_pos)
self.x = self.center_x
self.y = self.center_y
self.pos = (self.x, self.y)
self.rect_bg.pos = self.pos
def update_graphics_pos(self, instance, value):
self.rect_bg.pos = value
def setSize(self, width, height):
self.size = (width, height)
def setPos(self, xpos, ypos):
self.x = xpos
self.y = ypos
class ScoreWidget(Widget):
def __init__(self, **kwargs):
super(ScoreWidget, self).__init__(**kwargs)
self.asteroidScore = 0
self.currentScore = 0
with self.canvas:
tmpPos = (Window.width*0.25, Window.height*0.25)
tmpSize = (Window.width*0.5, Window.height*0.5)
Color(0.1, .1, .1)
self.scoreRect = Rectangle(pos=tmpPos, size=tmpSize)
def prepare(self):
#calculate the score
try:
self.finalScore = self.asteroidScore*100
except:
print 'problems getting score'
self.animateScore()
def animateScore(self):
#display score at 0 and every time interval add 100 until
#we reach the final score
#draw a score widget and schedule updates
scoreText = 'Score: 0'# + str(self.finalScore)
self.scoreLabel = Label(text=scoreText,font_size = '20sp')
self.scoreLabel.x = Window.width*0.3
self.scoreLabel.y = Window.height*0.3
self.add_widget(self.scoreLabel)
Clock.schedule_once(self.updateScore, .1)
self.drawStars()
def updateScore(self,dt):
self.currentScore = self.currentScore +100
self.scoreLabel.text = 'Score: ' + str(self.currentScore)
if self.currentScore < self.finalScore:
Clock.schedule_once(self.updateScore, 0.1)
def drawStars(self):
#0-10 asteroids 0 stars
#11-50 asteroids 1 star
#51-200 asteroids 2 stars
#201-500 asteroids 3 stars
#501-1000 asteroids 4 stars
#1001+ asteroids 5 stars
starNumber = 0
if self.asteroidScore > 10:
starNumber = 1
if self.asteroidScore > 50:
starNumber = 2
if self.asteroidScore > 200:
starNumber = 3
if self.asteroidScore > 500:
starNumber = 4
if self.asteroidScore > 1000:
starNumber = 5
with self.canvas:
#draw stars
#rect one
starPos = Window.width*0.27, Window.height*0.42
starSize = Window.width*0.06,Window.width*0.06
starString = 'gold_star.png'
if starNumber < 1:
starString = 'gray_star.png'
starRectOne = Rectangle(source=starString,pos=starPos, size = starSize)
#rect two
starPos = Window.width*0.37, Window.height*0.42
if starNumber < 2:
starString = 'gray_star.png'
starRectTwo = Rectangle(source=starString,pos=starPos, size = starSize)
#rect three
starPos = Window.width*0.47, Window.height*0.42
if starNumber < 3:
starString = 'gray_star.png'
starRectThree = Rectangle(source=starString,pos=starPos, size = starSize)
#rect four
starPos = Window.width*0.57, Window.height*0.42
if starNumber < 4:
starString = 'gray_star.png'
starRectFour = Rectangle(source=starString,pos=starPos, size = starSize)
#rect five
starPos = Window.width*0.67, Window.height*0.42
if starNumber < 5:
starString = 'gray_star.png'
starRectFive = Rectangle(source=starString,pos=starPos, size = starSize)
class Asteroid(WidgetDrawer):
#Asteroid class. The flappy ship will dodge these
imageStr = './sandstone_1.png'
rect_bg = Rectangle(source=imageStr)
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
def move(self):
self.x = self.x + self.velocity_x*5
self.y = self.y + self.velocity_y
def update(self):
self.move()
class Ship(WidgetDrawer):
#Ship class. This is for the main ship object.
#velocity of ship on x/y axis
#setup constants, health, etc
#choose default image:
impulse = 3
grav = -0.1
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
flameSize = (Window.width*.03, Window.width*.03)
def move(self):
self.x += self.velocity_x
self.y += self.velocity_y
#don't let the ship go too far
if self.y < Window.height*0.05:
#give upwards impulse
Clock.unschedule(self.update)
self.explode()
if self.y > Window.height*0.95:
Clock.unschedule(self.update)
self.explode()
def checkBulletNPCCollision(self, j):
if self.k.collide_widget(j):
j.health = j.health - self.k.bulletDamage
j.attackFlag = 'True'
#age the bullet
self.k.age = self.k.lifespan+10
def checkBulletStageCollision(self,q):
if self.k.collide_widget(q):
#if object type is asteorid
try:
if q.type == 'asteroid':
q.health = q.health - self.k.bulletDamage
self.k.age = self.k.lifespan+10
except:
print 'couldnt hit asteroid'
def determineVelocity(self):
#move the ship up and down
#we need to take into account our acceleration
#also want to look at gravity
self.grav = self.grav*1.05 #increase gravity
#set a grav limit
if self.grav < -4:
self.grav = -4
self.velocity_y = self.impulse + self.grav
self.impulse = 0.95*self.impulse
def drawArrow(self, *largs):
#draw the arrows directly onto the canvas
with self.canvas:
flamePos = (self.pos[0]-Window.width*.02, self.pos[1]+Window.width*.01)
flameRect = Rectangle(source='./flame.png', pos=flamePos, size=self.flameSize)
#schedule removal
def removeArrows(arrow, *largs):
self.canvas.remove(arrow)
Clock.schedule_once(partial(removeArrows, flameRect), .5)
Clock.schedule_once(partial(self.updateArrows, flameRect), 0.1)
def updateArrows(self, arrow, dt):
with self.canvas:
arrow.pos = (arrow.pos[0]-10, arrow.pos[1])
Clock.schedule_once(partial(self.updateArrows, arrow), 0.1)
return
def explode(self):
tmpSize = Window.width*0.25,Window.width*0.2
tmpPos = (self.x-Window.width*0.095, self.y-Window.width*0.08)
with self.canvas:
self.explosionRect = Rectangle(source ='./explosion1.png',pos=tmpPos,size=tmpSize)
def changeExplosion(rect, newSource, *largs):
rect.source = newSource
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion2.png'), 0.2)
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion3.png'), 0.4)
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion4.png'), 0.6)
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion5.png'), 0.8)
def removeExplosion(rect, *largs):
self.canvas.remove(rect)
Clock.schedule_once(partial(removeExplosion, self.explosionRect), 1)
def update(self):
self.determineVelocity()
self.move()
class GUI(Widget):
#this is the main widget that contains the game. This is the primary object
#that runs
asteroidList =[]
#important to use numericproperty here so we can bind a callback
#to use every time the number changes
asteroidScore = NumericProperty(0)
minProb = 1780
def __init__(self, **kwargs):
super(GUI, self).__init__(**kwargs)
#setup label for the score
self.score = Label(text = '0')
self.score.y = Window.height*0.8
self.score.x = Window.width*0.2
def check_score(self, obj):
#update credits
self.score.text = str(self.asteroidScore)
self.bind(asteroidScore = check_score)
self.add_widget(self.score)
#now we create a ship object
self.ship = Ship(imageStr = './ship.png')
self.ship.x = Window.width/4
self.ship.y = Window.height/2
self.add_widget(self.ship)
#self.ship.drawArrow()#start the flames
Clock.schedule_interval((self.ship.drawArrow), 0.1)
def addAsteroid(self):
#add an asteroid to the screen
#self.asteroid
imageNumber = randint(1, 4)
imageStr = './sandstone_'+str(imageNumber)+'.png'
tmpAsteroid = Asteroid(imageStr)
tmpAsteroid.x = Window.width*0.99
#randomize y position
ypos = randint(1, 16)
ypos = ypos*Window.height*.0625
tmpAsteroid.y = ypos
tmpAsteroid.velocity_y = 0
vel = 550#randint(10,25)
tmpAsteroid.velocity_x = -0.1*vel
self.asteroidList.append(tmpAsteroid)
self.add_widget(tmpAsteroid)
def drawTouchResponse(self,x,y):
#draw the arrows directly onto the canvas
with self.canvas:
tmpSize = Window.width*0.07, Window.width*0.07
tmpPos = (x-self.width/4,y-self.height/4)
self.arrowRect = Rectangle(source='./flame1.png',pos=tmpPos, size = tmpSize)
#schedule removal
def removeArrows(arrow, *largs):
self.canvas.remove(arrow)
def changeExplosion(rect, newSource, *largs):
rect.source = newSource
#schedule explosion two
Clock.schedule_once(partial(changeExplosion, self.arrowRect, './flame2.png'), 0.15)
#schedule explosion three
Clock.schedule_once(partial(changeExplosion, self.arrowRect, './flame3.png'), 0.3)
#schedule explosoin four
Clock.schedule_once(partial(changeExplosion, self.arrowRect, './flame4.png'), 0.45)
Clock.schedule_once(partial(removeArrows, self.arrowRect), 0.6)
#handle input events
def on_touch_down(self, touch):
self.ship.impulse = 3
self.ship.grav = -0.1
self.drawTouchResponse(touch.x, touch.y)
def showScore(self):
#this function will draw the score keeping widget, tabulate the score
#and rank with stars
self.scoreWidget = ScoreWidget()
self.scoreWidget.asteroidScore = self.asteroidScore #pass on score
self.scoreWidget.prepare()
self.add_widget(self.scoreWidget)
def removeScore(self):
self.remove_widget(self.scoreWidget)
def gameOver(self):
#add a restart button
restartButton = MyButton(text='Try Again')
#restartButton.background_color = (.5,.5,1,.2)
def restart_button(obj):
#reset game
self.removeScore()
for k in self.asteroidList:
self.remove_widget(k)
self.ship.xpos = Window.width*0.25
self.ship.ypos = Window.height*0.5
self.minProb = 1780
self.asteroidScore = 0
self.asteroidList = []
self.parent.remove_widget(restartButton)
Clock.unschedule(self.update)
Clock.schedule_interval(self.update, 1.0/60.0)
restartButton.size = (Window.width*.3,Window.width*.1)
restartButton.pos = Window.width*0.5-restartButton.width/2, Window.height*0.53
restartButton.bind(on_release=restart_button)
#we will want to bind the parent to listen for things from certain bubbles
#*** It's important that the parent get the button so you can click on it
#otherwise you can't click through the main game's canvas
self.parent.add_widget(restartButton)
#now draw the score widget
self.showScore()
def update(self, dt):
#This update function is the main update function for the game
#All of the game logic has its origin here
#events are setup here as well
#update game objects
#update ship
self.ship.update()
#update asteroids
#randomly add an asteroid
tmpCount = randint(1, 1800)
if tmpCount > self.minProb:
self.addAsteroid()
if self.minProb < 1300:
self.minProb = 1300
self.minProb -= 1
for k in self.asteroidList:
#check for collision with ship
if k.collide_widget(self.ship):
#game over routine
self.gameOver()
Clock.unschedule(self.update)
#add reset button
self.ship.explode()
k.update()
#check to see if asteroid is off of screen
if k.x < -100:
#since it's off the screen, remove the asteroid
self.remove_widget(k)
self.asteroidScore += 1
#remove asteroids off screen
tmpAsteroidList = self.asteroidList
tmpAsteroidList[:] = [x for x in tmpAsteroidList if (x.x > - 100)]
self.asteroidList = tmpAsteroidList
class ClientApp(App):
def build(self):
#this is where the root widget goes
#should be a canvas
self.parent = Widget() #
self.app = GUI()
#Start the game clock (runs update function once every (1/60) seconds
#Clock.schedule_interval(app.update, 1.0/60.0)
#add the start menu
self.sm = SmartStartMenu()
self.sm.buildUp()
def check_button(obj):
#check to see which button was pressed
if self.sm.buttonText == 'start':
#remove menu
self.parent.remove_widget(self.sm)
#start the game
print ' we should start the game now'
Clock.unschedule(self.app.update)
Clock.schedule_interval(self.app.update, 1.0/60.0)
try:
self.parent.remove_widget(self.aboutText)
except:
pass
if self.sm.buttonText == 'about':
self.aboutText = Label(text = 'Flappy Ship is made by Molecular Flow Games \n Check out: https://kivyspacegame.wordpress.com')
self.aboutText.pos = (Window.width*0.45,Window.height*0.35)
self.parent.add_widget(self.aboutText)
#bind a callback function that repsonds to event 'on_button_release' by calling function check_button
self.sm.bind(on_button_release = check_button)
#setup listeners for smartstartmenu
self.parent.add_widget(self.sm)
self.parent.add_widget(self.app) #use this hierarchy to make it easy to deal w/buttons
return self.parent
if __name__ == '__main__':
ClientApp().run()
This question already has answers here:
Create trails of particles for the bullets
(1 answer)
Pygame change particle color
(1 answer)
Closed 5 months ago.
I am a beginner with pygame. I want to include a few "Emitter" classes to "Particle" class to show points. It doesn't work and I don't know how to fix it. I think the problem is with gfxdraw. In the "Emitter" class, it initializes the system window. This can also be a problem. I don't know how to transfer this to the "Particle" class. Would it help at all? I used many combinations for repair. Nothing works. Please help. Does anyone have any idea?
import random
import sys
import pygame
from pygame.locals import *
import pygame.gfxdraw
class ParticleSystem:
def __init__(self, id, pos, a,b, width, height, radius, color):
self.id = id
self.width = width
self.height = height
self.radius = radius
self.color = color
self.g = 9.81
self.pos = [random.randint(a,b), 0]
self.enabled = True
self.df = 0
def update(self, time, collision):
if self.enabled:
v = 1 / float(time)
if not self.collision_detect(collision):
self.pos[1] += self.g*v
else:
self.enabled = False
if self.df != 0:
F = (self.g*v)/9
if self.df < 0:
F = -F
self.pos[0] += F
self.df -= F
def collision_detect(self, collision):
x = int(self.pos[0])
y = self.pos[1]
r = self.radius
points = collision[x-r:x+r]
for p in points:
if y + r >= p:
for i in range(x-r, x+r):
if i >= 0 and i < self.width:
collision[i] = y
return True
if self.pos[1] >= self.height:
return True
else:
return False
def draw(self, surface):
pygame.gfxdraw.filled_circle(surface, int(self.pos[0]), int(self.pos[1]), self.radius, self.color)
pygame.gfxdraw.aacircle(surface, int(self.pos[0]), int(self.pos[1]), self.radius, self.color)
class Emitter:
def __init__(self, a, b, height, width):
self.a = a
self.b = b
self.timer = 60
self.width = width
self.height = height
self.color = (255, 255, 255)
self.background_color = (0, 0, 0)
self.pos = [0,0]
self.particles = []
self.counter = 0
self.freq = 5
self.size = 4
self.collision = [height] * width
self.df_c = 1
self.df_f = 100
self.begin()
def begin(self):
self.screen = pygame.display.set_mode((self.width, self.height))
self.clock = pygame.time.Clock()
while 1:
time = self.clock.get_time()
self.update(time)
self.clock.tick(self.timer)
self.render(time)
def update(self, time):
if self.counter > self.freq:
self.counter = 0
particle = ParticleSystem(len(self.particles), self.pos, self.a, self.b, self.width, self.height, self.size, self.color)
self.particles.append(particle)
else:
self.counter += 1
df_c = random.randint(0, 100)
df_f = 0
if df_c <= self.df_c:
df_f = random.randint(-self.df_f, self.df_f)
for part in self.particles:
if part.enabled:
if df_f != 0:
part.df = df_f
part.update(time, self.collision)
def render(self, time):
surface = pygame.Surface(self.screen.get_size())
surface.convert()
surface.fill(self.background_color)
for part in self.particles:
part.draw(surface)
self.screen.blit(surface, (0, 0))
pygame.display.flip()
class Particle:
def __init__(self, width, height, caption):
self.width = width
self.height = height
self.caption = caption
self.initialization()
def initialization(self):
pygame.init()
pygame.display.set_caption(self.caption)
#Emitter(0, self.width, self.height, self.width)
Emitter(0, 125, self.height, self.width)
Emitter(200, 500, self.height, self.width) #how to show the second Emitter
self.input(pygame.event.get())
def input(self, events):
for event in events:
if event.type == pygame.QUIT:
sys.exit(0)
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit(0)
if __name__ == "__main__":
particle = Particle(800, 600, "Particle System - Christmas Time")
I have been following a guide to make my game. However, when I try to run the program I get a syntax Error with the Class Circle and not sure why? I can't figure out the reason and I'm sure there are more issue afterward the Class Circle but can't fix it.
import pygame
from pygame.locals import *
from uagame import Window
import time
import random
def main():
window = Window('Pong', 500, 400)
window.set_auto_update(False)
game = Game(window)
game.play()
window.close()
class Rect:
def __init__(self, center, radius, color, window):
self.center = center
self.radius = radius
self.color = color
self.window = window
def draw(self):
pygame.draw.rect(self.window.get_surface(), self.color, Rect((100, 200), (20, 30))
class Circle: # Syntax Error: class Circle:: <string>, line 28, pos 5
def __init__(self, center, radius, color, window):
self.center = center
self.radius = radius
self.color = color
self.window = window
def draw(self):
pygame.draw.circle(self.window.get_surface(), self.color, self.center,self.radius)
def get_color(self):
return self.color
def move(self):
window_size = (self.window.get_width() , self.window.get_height())
for index in range(0,2):
self.center[index] = (self.center[index] + 1) % window_size[index]
def enlarge(self,increment):
self.radius = self.radius + increment
class Game:
def __init__(self, window):
self.window = window
self.bg_color = pygame.Color('black')
self.fg_color = 'green'
self.rect_color = 'green'
self.pause_time = 0.02
self.close_clicked = False
self.continue_game = True
self.circle = Ball([200,200], 20, pygame.Color(self.fg_color),window)
self.rect = Rect([100,200], 50 , pygame.Color(self.fg_color),window)
self.radius_increment = 5
def play(self):
while not self.close_clicked:
self.handle_event()
self.draw()
if self.continue_game:
self.update()
self.decide_continue()
time.sleep(self.pause_time)
def handle_event(self):
event = pygame.event.poll()
if event.type == QUIT:
self.close_clicked = True
elif event.type == MOUSEBUTTONUP and self.continue_game:
self.handle_mouse_up()
def handle_mouse_up(self):
self.circle.enlarge(self.radius_increment)
def draw(self):
self.window.clear()
self.circle.draw()
self.rect.draw()
if not self.continue_game:
self.window.set_bg_color(self.fg_color)
self.window.clear()
self.window.update()
def update(self):
self.circle.move()
def decide_continue(self):
pass
main()
Your error actually lies in the line above. See that your code is missing a parenthesis to close the rect() function. Always remember to count the parenthesis on long functions like this.
def draw(self):
pygame.draw.rect(self.window.get_surface(), self.color, Rect((100, 200), (20, 30))) #<-
This code displays the image assassin1.png on a black screen standing still at position (50, 80).
The goal is to induce the pymunk gravity on that image so that it falls down. I followed the pymunk tutorial which is written using pygame and tried to adapt that. I don't know why my code isn't applying gravity to the my image. Could someone tell me what I am doing wrong and what I should change to make it work properly?
import pyglet
import pymunk
class PymunkSpace(object):
def assassin_space(self, space):
self.space = space
self.mass = 91
self.radius = 14
self.inertia = pymunk.moment_for_circle(self.mass, 0, self.radius)
self.body = pymunk.Body(self.mass, self.inertia)
self.body.position = 50, 80
self.shape = pymunk.Circle(self.body, self.radius)
self.space.add(self.body, self.shape)
return self.shape
class Assassin(pyglet.sprite.Sprite):
def __init__(self, batch, img, x, y):
pyglet.sprite.Sprite.__init__(self, img, x , y )
class Game(pyglet.window.Window):
def __init__(self):
pyglet.window.Window.__init__(self, width = 315, height = 220)
self.batch_draw = pyglet.graphics.Batch()
self.a_space = PymunkSpace().assassin_space(space)
self.player1 = Assassin(batch = self.batch_draw, img = pyglet.image.load("assassin1.png"), x = self.a_space.body.position.x ,y = self.a_space.body.position.y )
def on_draw(self):
self.clear()
self.batch_draw.draw()
self.player1.draw()
space.step(1/50.0)
if __name__ == "__main__":
space = pymunk.Space()
space.gravity = (0.0, -900.)
window = Game()
pyglet.app.run()
The problem is that you set the location of your Sprite once:
self.player1 = Assassin(batch = self.batch_draw, img = pyglet.image.load("assassin1.png"), x = self.a_space.body.position.x ,y = self.a_space.body.position.y )
class Assassin(pyglet.sprite.Sprite):
def __init__(self, batch, img, x, y):
pyglet.sprite.Sprite.__init__(self, img, x , y )
but this location never gets updated.
Another problem is that in the pygame example, space.step(1/50.0) is called every iteration of the main loop, while your's only gets called when the window is drawn.
So, first, you should ensure space.step gets called every frame. Do so by using a clock:
class Game(pyglet.window.Window):
def __init__(self):
...
pyglet.clock.schedule(self.update)
def on_draw(self):
self.clear()
self.batch_draw.draw()
self.player1.draw()
def update(self, dt):
space.step(dt)
The next step is ensuring the location of the sprite is keeped in sync with the pyglet object.
Change your Assassin class:
class Assassin(pyglet.sprite.Sprite):
def __init__(self, batch, img, space):
self.space = space
pyglet.sprite.Sprite.__init__(self, img, self.space.body.position.x , self.space.body.position.y)
def update(self):
self.x = self.space.body.position.x
self.y = self.space.body.position.y
Create your Assassin instance like this:
class Game(pyglet.window.Window):
def __init__(self):
pyglet.window.Window.__init__(self, width = 315, height = 220)
self.batch_draw = pyglet.graphics.Batch()
self.a_space = PymunkSpace().assassin_space(space)
self.player1 = Assassin(batch = self.batch_draw, img = pyglet.image.load("assassin1.png"), space = self.a_space)
pyglet.clock.schedule(self.update)
and call self.player1.update() in your new update method:
class Game(pyglet.window.Window):
def __init__(self):
...
def on_draw(self):
...
def update(self, dt):
self.player1.update()
space.step(dt)
(And you should probably get rid of some unneeded instance members, but that's another topic)