dynamic body with segment shape do not move with a joint - python

In pymunk, I set the body mass and the space gravity, it should automatic fall down, but the body with segment shape do not move, here is my code in pyglet
import pyglet
import pymunk
from pymunk.pyglet_util import DrawOptions
window = pyglet.window.Window(1280,720,resizable=False)
options = DrawOptions()
space = pymunk.Space()
space.gravity = 0,-1000
ball_mass = 1
ball_radius = 10
ball_moment = pymunk.moment_for_circle(ball_mass,0,10)
ball = pymunk.Body(ball_mass,ball_moment)
ball_shape = pymunk.Circle(ball,ball_radius)
ball.position =200,500
ball_shape.elasticity = 1
ball_shape.friction = 1
space.add(ball,ball_shape)
stick = pymunk.Body(1,100,body_type=pymunk.Body.DYNAMIC)
stick_shape = pymunk.Segment(stick,(0,0),(150,150),4)
stick.position = (300,400)
pin = pymunk.PivotJoint(space.static_body,stick,(300,400))
# stick.apply_impulse_at_local_point((0,-100),(150,150))
space.add(stick,stick_shape,pin)
#window.event
def on_draw():
window.clear()
space.debug_draw(options)
def update(dt):
space.step(dt)
if __name__ == '__main__':
pyglet.clock.schedule_interval(update, 1/60.0)
pyglet.app.run()
the ball fall down but the stick do not move, or apply_impulse_at_local_point on stick, I just wondering, if there is no PivotJoint, the stick fall down, I just pin one point of the stick, why it does not move, it should rotate, is it?

One problem is that the stick has its center of gravity at one end. That make it behave a bit strange. Try making it have its center of gravity in its actual center instead.
stick_shape = pymunk.Segment(stick,(-75,-75),(75,75),4)

Related

How do I get glsl shader to work with pymunk physics engine and camera?

Here are my observations of the problem:
The asteroids game uses a drawn texture sprite for the glow ball weapons. This is done in the GlowBall class. Weapon 7 uses a drawn image
The drawn sprite is with bullet_list.draw() but the shader: for bullet in bullet_list: bullet.draw().
The sprite is being drawn correctly but the shader is not.
The shader seems to move with the ship but faster. almost like it is being drawn to a larger area.
What I tried:
Using the player position minus the camera position which is what I
did to make a drawn image stay on the player - to draw a turret on
the top of the ship.
Tried to make a second version of Shadertoy.py so that I can use the
screen_rectangle instead of quad_2d_fs.
If I use the pymunk physics engine it can accelerate the shader but
causes it to be drawn in the way described above.
How can I use the camera with the shader class along with the pymunk physics engine to create plasma bullets and explosions?
These classes come from the arcade example: https://github.com/pythonarcade/asteroids
I am using a margin scrolling from example: https://api.arcade.academy/en/latest/examples/sprite_move_scrolling_box.html#sprite-move-scrolling-box
'''
class GlowBullet(arcade.Sprite):
def __init__(self, image_file=None, scale=1.0, shadertoy=None, player_no=1):
super().__init__(image_file, scale)
self.type = None
self.shadertoy = shadertoy
self.player_no = player_no
def draw(self):
pass
class GlowBall(GlowBullet):
def __init__(self, shadertoy, glowcolor, radius):
super().__init__(shadertoy=shadertoy)
self.type = None
self.shadertoy = shadertoy
self.glowcolor = glowcolor
self.texture = arcade.make_circle_texture(radius * 2, glowcolor)
self._points = self.texture.hit_box_points
def draw(self):
self.shadertoy.program['pos'] = self.position
self.shadertoy.program['color'] = arcade.get_three_float_color(self.glowcolor)
self.shadertoy.render()
'''
...
In the GameView class:
'''
class GameView(arcade.View):
def __init__(self):
super().__init__()
self.glowball_shadertoy = Shadertoy.create_from_file(self.window.get_size(), GLOW_BALL_IMAGE)
def player_blaster(self,x,y,blaster_filename):
self.click_x = self.world_mouse_x
self.click_y = self.world_mouse_y
r,diff_angle = self.cart_to_polar([self.player.center_x,self.player.center_y],[self.world_mouse_x,self.world_mouse_y])
bullet_sprite = GlowBall(glowcolor=bullet_color,
radius=5,
shadertoy=self.glowball_shadertoy)
glow_bullet = GlowBullet(glowcolor=(1000, 1000, 1000),radius=7,shadertoy=self.glowball_shadertoy)
player_size = max(self.player.width, self.player.height) / 2
bullet_size = max(bullet.width, bullet.height) / 2
glow_bullet_size = bullet_size
radius_start = player_size + bullet_size
glow_radius_start = 0
glow_bullet_x,glow_bullet_y = self.polar_to_cart(glow_radius_start,diff_angle,[self.player.center_x-self.camera_gui.position[0],self.player.center_y-self.camera_gui.position[1]])
glow_bullet_x,glow_bullet_y = self.polar_to_cart(glow_radius_start,diff_angle,[self.player.center_x,self.player.center_y])
glow_bullet.center_x=glow_bullet_x
glow_bullet.center_y=glow_bullet_y
glow_bullet.angle=math.degrees(diff_angle) - 90
bullet.damage = self.player.damage_attack
self.bullet_list.append(bullet)
self.glow_bullet_list.append(glow_bullet)
angle = math.pi/2
bullet_force_x = PLASMA_FORCE_1 * math.cos(angle)
bullet_force_y = PLASMA_FORCE_1 * math.sin(angle)
self.physics_engine.add_sprite(glow_bullet,
mass=0.01,
damping=1,
friction=0.01,
moment_of_intertia=100000,
elasticity=0.9)
self.physics_engine.apply_force(glow_bullet, (bullet_force_x,bullet_force_y))
def on_draw(self):
""" Draw everything """
self.clear()
# This controlls the GLSL shader
for bullet in self.glow_bullet_list:
bullet.draw()
# This draws the sprite
self.glow_bullet_list.draw()
'''
Did you run it in a HiDPI environment?
If so, you should use shader class with proper framebuffer size. Not window.get_size()
Like,
shader = ShaderToy(window.get_framebuffer_size())
render_scale = window.get_framebuffer_size()[0] / window.size[0]
shader.program['pos'] = position * render_scale

3rd Person POV for Game Ursina

I am trying to make an rpg-style game with ursina. I want to have the camera always follow the back of the character. I tried using camera.look_at(player) but I couldn't get the camera to rotate to the back of the character when it rotated.
app = Ursina()
class character(Entity):
def __init__(self):
super().__init__(
model = load_model('cube'),
color = color.red,
position = (-0, -3, -8)
)
player = character()
print(player.forward)
print(player.forward)
camera.look_at(player)
player.rotation_y =180
def update():
if held_keys['a']:
player.rotation_y -= 2
if held_keys['d']:
player.rotation_y += 2
app.run()```
You may want to change the origin. Also using parents. I'll explain what this means in a moment.
To change the origin (the point at which an entity is moved and rotated) to a point behind it.
e.g.
from ursina import * # import urisna
app = Ursina() # make app
player = Entity(model='cube', # this creates an entity of a cube
origin = (0, 0, -2)) #now you have a origin behind the entity
app.run() #run app
But what about the camera, I hear you ask!
I'd recommend the ursina.prefabs.first_person_controller
It may be designed for 1st person control, but you can use it for your purpose.
# start by doing the normal thing
from ursina import *
# but also import the first person prefab
from ursina.prefabs.first_person_controller import FirstPersonController
app = Ursina()
# create camera and player graphic
cam = FirstPersonController()
player = Entity(model='cube',
origin = (0, 0, -2),
parent = cam)
# run
app.run()
You will need to create a floor entity.
That is ALL YOU NEED for the 3rd person controller. The parents and origins ensure that. It has built in WASD and Arrow Key control, with mouse control too.
#Cyber-Yosh recently asked a question for this post on how to use it without the 1st person controller. Here's how. I have commented on the changes.
from ursina import * # import as usual
app = Ursina() # create app as usual
window.fps_counter.enabled = False # this is just to remove the fps counter
box = Entity(model='cube', # create cube as before (you can make your own class)
origin=(0,0.7,-5), # set origin to behind the player and above a little
parent=camera, # make it orientate around the camera
color=color.red, # change the color
texture='shore') # choose a nice texture
def update(): # define the auto-called update function
if held_keys['a']:
camera.rotation_y -= 10 * time.dt # the time.dt thing makes it adapt to the fps so its smooth
elif held_keys['d']:
camera.rotation_y += 10 * time.dt
Sky() # just a textured sky to make sure you can see that you are both rotating
app.run() # run
You'll notice that I've not created a class (adapting this for it is easy enough), but I did not use load_model. This is because even if you are using your own model, you don't need to use load_model. Simply put the name of the file (without the file extension) as a string. This works, I've tried it.
If you have any more questions, don't hesitate to ask. I am more than happy to help. If this worked, be sure to upvote and approve.
If anyone wants to use FirstPersonController, I found out how to move the camera away from the player, while keeping all physics interactions!
After creating the FirstPersonController, modify the position of its camera_pivot (default is (0,2,0)).
player = FirstPersonController(model="cube", color=color.orange, origin_y=-.5,z=-10)
# the default camera_pivot is (0,2,0)
player.camera_pivot.z = -3.5 # move the camera behind the player model
player.camera_pivot.y = 2.5 # move the camera a little higher
Full example (q to quit)
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.shaders import lit_with_shadows_shader
app = Ursina()
Entity.default_shader = lit_with_shadows_shader
ground = Entity(model='plane', collider='box', scale=64, color=color.green)
player = FirstPersonController(model="cube", color=color.orange, origin_y=-.5,z=-10)
# the default camera_pivot is (0,2,0)
player.camera_pivot.z = -3.5 # move the camera behind the player model
player.camera_pivot.y = 2.5 # move the camera a little higher
# setting collider and making it visible for debugging
player.collider = BoxCollider(player, Vec3(0,1,0), Vec3(1,2,1))
player.collider.visible=True
# adding some objects to collide with
for i in range(16):
Entity(model='cube', origin_y=-.5, scale=2, texture='brick', texture_scale=(1,2),
x=random.uniform(-8,8),
z=random.uniform(-8,8) + 8,
collider='box',
scale_y = random.uniform(2,3),
color=color.hsv(0, 0, random.uniform(.9, 1))
)
sun = DirectionalLight()
sun.look_at(Vec3(1,-1,-1))
Sky()
def input(key):
if key == 'q':
exit()
app.run()
Parent the camera to the player and move it back. That way it will rotate along with the player entity.
camera.parent = player
camera.z = -10
I have a better solution, because when using the FirstPersonController, the physics are applied to the controller and not to the player, my solution is to create a camera:
camera= EditorCamera()
this will create a camera that will allow us to see the game, the next thing we must do is we must create the player and the floor:
terrain_width= 50
player= Entity(model="cube", color= color.orange)
player.x= 0
floor= Entity(model="plane", texture="grass", scale= terrain_width)
Now that this is done, we will stick the camera to the player, and adjust some parameters to see the player from above-back:
camera.parent= player
camera.y= 5
camera.z= -10
camera.rotation_x= 9.15
now we can make our player move and we will see that the camera also moves:
def input(key):
if key == "a":
player.x-= 1
if key == "d":
player.x+= 1
if key == "w":
player.z+= 1
if key == "s":
player.z-= 1
this would be the complete code:
from ursina import *
app= Ursina()
camera= EditorCamera()
terrain_width= 50
player= Entity(model="cube", color= color.orange)
player.x= 0
floor= Entity(model="plane", texture="grass", scale= terrain_width)
camera.parent= player
camera.y= 5
camera.z= -10
camera.rotation_x= 9.15
def input(key):
if key == "a":
player.x-= 1
if key == "d":
player.x+= 1
if key == "w":
player.z+= 1
if key == "s":
player.z-= 1
app.run()
I hope this will be useful to you :)

Explosions Sprite Despears Way To Fast When Enemy is Killed How Do Fix This?

on my draw I did this but the explosion doesnt event last a sec and just dispears
is there a better way I could do this instead of saying if the enemy health is greater then this load this?
for enemys in enemying:
if enemys.health < -4
for explode in explodes:
explode.draw((enemys.hitbox, enemys.hitbox))
my explosion class
class explode:
def __init__(self,x,y,height,width,color):
self.x = x
self.y = y
self.height = height
self.width = width
self.explode = [
pygame.image.load("spark_01.png"),
pygame.image.load("spark_02.png"),
pygame.image.load("spark_03.png"),
pygame.image.load("spark_04.png"),
pygame.image.load("spark_05.png"),
pygame.image.load("spark_06.png"),
pygame.image.load("spark_07.png")]
self.explode = [pygame.transform.scale(image,(image.get_width()//5,image.get_height()//5)) for image in self.explode]
self.rect = pygame.Rect(x,y,height,width)
self.direction = "blobright"
self.anim_index = 0
def draw(self,x):
self.rect.topleft = (self.x,self.y)
if self.direction == "blobright":
window.blit(self.explode[self.anim_index], self.rect)
self.anim_index += 1
if self.anim_index == len(self.explode):
self.anim_index = 0
black = (0,0,0)
explode1 = explode(400,450,50,50,black)
explodes = [explode1]
this is where I delete the enemys
for enemyshoot in enemyshooting:
for bullet in bullets:
if bullet.rect.colliderect(enemyshoot.hitbox):
if enemyshoot.health > -8:
enemyshoot.health -= 1
bullets.pop(bullets.index(bullet))
else:
del enemyshooting[one]
Here is the example I promised. Notice how the system clock and a flag
are used to control animation speed so that different actions can happen on the screen at different speeds. This program is written quick-and-dirty. There are MUCH more efficient ways to handle the animation so that the screen is only drawn to when it is time for something to move, and ONLY those parts of the screen that have changed are drawn and refreshed. But don't worry about that. The lesson here is how to use the clock to slow down your animation speed. What's the old saying? Code now, refactor later? Words to live by.
import pygame
from pygame.locals import * # Pygame Constants such as FULLSCREEN and KEYDOWN.
from pygame import Color
pygame.init()
import time
# ==============================================================
# Disable Windows Auto-scaling
# The windows auto-scaling feature can mess with pygame graphics.
# Windows-Specific: Delete if you are not on a Windows machine
# If you are on Windows, comment section to see the effect.
import ctypes
awareness = ctypes.c_int()
errorCode = ctypes.windll.shcore.GetProcessDpiAwareness(0, ctypes.byref(awareness))
errorCode = ctypes.windll.shcore.SetProcessDpiAwareness(2) # Awareness levels can be 0, 1 or 2:
# ===============================================================
# I will base the example on your explode class. If I were writing the
# game, I would use a sprite class along with a sprite group to simplify
# the code. However, these topics are an entirely different branch of study
# so I won't use them in the example. Forgive my quick-and-dirty code.
# I don't have your image files, but it looks as though
# you have an actual animation to use for your explosion that is
# made up of several different frames. We will emulate this for the
# sake of our demonstration.
class EXPLODE:
def create_spark(self):
TRANSPARENT = (254,255,255) # You can chose your own transparent color.
# Lacking the spark files, let's draw our own.
spark_img = pygame.Surface((31,31))
spark_img.fill(TRANSPARENT)
spark_img.set_colorkey(TRANSPARENT)
spark_rec = spark_img.get_rect()
left_center = (spark_rec.left,spark_rec.centery)
right_center = (spark_rec.right,spark_rec.centery)
top_center = (spark_rec.centerx,spark_rec.top)
bottom_center = (spark_rec.centerx,spark_rec.bottom)
top_left = spark_rec.topleft
top_right = spark_rec.topright
bottom_left = spark_rec.bottomleft
bottom_right = spark_rec.bottomright
pygame.draw.circle(spark_img,Color("yellow"),spark_rec.center,spark_rec.width//2,0)
pygame.draw.line(spark_img,Color("indianred"),left_center,right_center,2)
pygame.draw.line(spark_img,Color("red"),top_center,bottom_center,2)
pygame.draw.line(spark_img,Color("burlywood3"),top_left,bottom_right,3)
pygame.draw.line(spark_img,Color("orange"),top_right,bottom_left,3)
# Now crop line segments that fall outside the spark circle..
pygame.draw.circle(spark_img,TRANSPARENT,spark_rec.center,spark_rec.width//2+8,8)
return spark_img
def __init__(self,window,x,y,height,width,color = (255,0,0)):
self.window = window # Needed so class can draw to the screen.
self.T0 = time.time() # Holds the starting time for the timer.
self.x = x
self.y = y
self.height = height
self.width = width
self.anim_index = 0
image = self.create_spark()
# Standing in for actual animation pages, we scale up the spark image.
self.explode = [
image,
pygame.transform.scale(image,(image.get_width()*2,image.get_height()*2)),
pygame.transform.scale(image,(image.get_width()*4,image.get_height()*4)),
pygame.transform.scale(image,(image.get_width()*6,image.get_height()*6)),
pygame.transform.scale(image,(image.get_width()*8,image.get_height()*8))]
'''
# Or you can load your spark frames as before.
self.explode = [
pygame.image.load("spark_01.png"),
pygame.image.load("spark_02.png"),
pygame.image.load("spark_03.png"),
pygame.image.load("spark_04.png"),
pygame.image.load("spark_05.png"),
pygame.image.load("spark_06.png"),
pygame.image.load("spark_07.png")]
# All of the loaded images are scaled down to 1/5 their size.
self.explode = [pygame.transform.scale(image,(image.get_width()//5,image.get_height()//5)) for image in self.explode]
'''
self.rect = image.get_rect()
self.direction = "blobright" # <-- I don't know what this is, so I've ignored it.
self.anim_index = 0
def draw(self,enemy_rec,anin_speed): # Create an animation method to handle the draw routine.
clock = time.time()
elapsed_time = clock - self.T0
finish_flg = False
if elapsed_time > anin_speed: # Animation Speed Controlled Here!!
self.anim_index +=1
self.T0 = time.time() # Reset the start time.
if self.anim_index == len(self.explode)-1:
finish_flg = True
frame = self.explode[self.anim_index]
rec = frame.get_rect()
rec.center = enemy_rec.center
self.window.blit(frame,rec)
return finish_flg # The finish flag lets the main program know it can delete the enemy.
# ================== MAIN() ===================
# ----------------------------------------------
def main(): # By using a 'main()' function, your code will run faster!
screen = pygame.display.set_mode((3000,2000),pygame.FULLSCREEN,32)
screen_rec = screen.get_rect()
screen.fill(Color("steelblue1"))
# Declare and initialie an instance of the EXPLODE class.
# Only one enemy can blow up at the same time for any give instance
# of the class, so you may want each ship to have its own instance
# if there is a chance of simultanious explosions.
# I wouldn't normally use this aproach, but I wanted to stay
# as close as possible to your existing code.
explode = EXPLODE(screen,screen_rec.centerx,screen_rec.centery,30,20,Color('blue'))
# Let's create some "enemy" units.
# You would use an enemy class for this (and for drawing them)
# but this example is qick and dirty, so.. Two enemies coming up!
# One enemy to blow up when it hits the wall.
enemy1_img = pygame.Surface((30,30))
enemy1_rec = enemy1_img.get_rect()
enemy1_img.fill(Color("Green"))
pygame.draw.rect(enemy1_img,Color("Red"),enemy1_rec,5)
# And one 'enemy' to move while the other is blowing up.
enemy2_img = enemy1_img.copy()
enemy2_rec = enemy2_img.get_rect()
# Give enemies screen positions.
enemy1_rec.center = (screen_rec.centerx-300, screen_rec.centery-300)
enemy2_rec.center = (screen_rec.centerx-800,screen_rec.centery-300)
# Create a wall for a ship to crash into.
wall_img = pygame.Surface((100,60))
wall_img.fill(Color("Indianred"))
wall_rec = wall_img.get_rect()
wall_rec.center = screen_rec.center
wall_rec = wall_rec.move((400,0))
# Oh, this list is ugly. Forgive me! Used instead of a list of Enemy-Class objects.
enemy_list = [[10,enemy1_img,enemy1_rec,(2,1)],[10,enemy2_img,enemy2_rec,(3,0)]] # [Life, Image, Rectangle, Speed]
# Ok, the setup is finished. Time for some action!
# =============== BODY ===================
# ------------------------------------------
anin_speed = 0.3 # You can control explosion speed here!
pygame.mouse.set_visible(False)
run_cycle = True
while run_cycle == True:
# There are much better ways to erase images, but this will do for now.
screen.fill(Color("steelblue1")) # Erase old sprites.
screen.blit(wall_img,wall_rec) # Put the wall back in place.
# Because it is bad idea to modify an object being looped through,
# we will construct a new list, called 'hold', and copy it back at the end.
hold = []
for enmy in enemy_list:
life,enemy_img,enemy_rec,speed = enmy
if life > 4:
screen.blit(enemy_img,enemy_rec) # If enemy is healthy, put it on the screen.
enemy_rec = enemy_rec.move(speed)
if enemy_rec.colliderect(wall_rec) == True:
life = 0
if enemy_rec.left > screen_rec.right+10: # End the program after top ship leaves the screen.
run_cycle = False
hold.append([life,enemy_img,enemy_rec,speed])
else: # Otherwise draw the explosion.
finish_flg = explode.draw(enemy_rec,anin_speed)
if finish_flg == False: # If TRUE the enemy is ommitted from the hold list!
hold.append(enmy)
enemy_list = hold.copy() # And now the possibly modified list is copied back to the enemy_list.
pygame.display.flip()
# ================
# Main
# ----------------
main() # Hint! For technical reasons related to the compiler being able
# to control scope and predict variable sizes, keeping your
# main body encapsulated in a function like this will improve
# efficiency and run speeds.
You have a great start. If nothing needs to happen while the explosion is going on, you could use a sleep command in your loop. >> time.sleep(0.01)
If the action has to continue on the screen during the explosion, then you will need to use a timer and keep returning to that function after each duration to draw the next frame. Just initialize using >> T0 = time.time() before the explosion, visit the function when time.time()-T0 > 0.01 seconds (for example) and reset T0 = time.time() after each frame is drawn. Return a 'finished' value when the animation is over, so you can remove it from your enemy list.
In the __init__() for explode note the time when it is called and save it.
In the explodes draw() only increment self.anim_index when enough time has passed since the last time it was incremented (based on the time value you saved in the __init__()). This will let you go more slowly through the frames of the exploding animation.
There is really no difference between this and any other object animation, other than once the cycle completes the object (the explosion) goes a way.

How to make an image move through a pyglet window?

I am trying to make an animation using pyglet. So first I tried a simple animation, moving the image in a strait line. Idealy I would like it to bounce around from left to right.
Here is my code:
import pyglet
def center_image(image):
"""Sets an image's anchor point to its center"""
image.anchor_x = image.width // 2
image.anchor_y = image.height // 2
# Make window.
window = pyglet.window.Window(width=640, height=480)
# Load image.
pyglet.resource.path = ['images']
pyglet.resource.reindex()
heart_img = pyglet.resource.image('red-heart.png')
center_image(heart_img)
# Make animation sprite.
heart_grid = pyglet.image.ImageGrid(heart_img, rows=1, columns=5)
heart_ani = pyglet.image.Animation.from_image_sequence(heart_grid, duration=0.1)
heart_sprite = pyglet.sprite.Sprite(heart_ani, x=100, y=300)
heart_sprite.update(scale=0.05)
#window.event
def on_draw():
window.clear()
heart_sprite.draw()
if __name__ == '__main__':
pyglet.app.run()
This code produces this:
How can I make the whole heart move through the window?
The desired trajectory of the heart would be something like this:
Where the box is the frame, the arches are the trajectory and O is a sprite. So the heart would bounce of the first letter of each word and then bounce of the sprite.
So the main issue is that Animation assumes a series of images within a large image. It's called sprite animations and it's essentially just a series strip (usually in a row or a grid pattern) of the movements you want. It's useful for animating walking, attacking and other similar game mechanics.
But to move an object around the canvas, you would need to manipulate the vertices or the image location manually in some way. Your own solution works on the principle of checking if X is greater or less than min and max restrictions. And I would just like to add ontop of that to show some techniques to make it easier and faster to work with the movements and directions. Below I've worked with bitwise operations to determain the direction of movement and this makes the heart bounce around the parent (window) constraints of width and height.
I've also taken the liberty to make the whole project more object oriented by inheriting the pyglet Window class into one object/class as well as make heart it's own class to easier separate what is called when and on what object.
from pyglet import *
from pyglet.gl import *
key = pyglet.window.key
# Indented oddly on purpose to show the pattern:
UP = 0b0001
DOWN = 0b0010
LEFT = 0b0100
RIGHT = 0b1000
class heart(pyglet.sprite.Sprite):
def __init__(self, parent, image='heart.png', x=0, y=0):
self.texture = pyglet.image.load(image)
pyglet.sprite.Sprite.__init__(self, self.texture, x=x, y=y)
self.parent = parent
self.direction = UP | RIGHT # Starting direction
def update(self):
# We can use the pattern above with bitwise operations.
# That way, one direction can be merged with another without collision.
if self.direction & UP:
self.y += 1
if self.direction & DOWN:
self.y -= 1
if self.direction & LEFT:
self.x -= 1
if self.direction & RIGHT:
self.x += 1
if self.x+self.width > self.parent.width:
self.direction = self.direction ^ RIGHT # Remove the RIGHT indicator
self.direction = self.direction ^ LEFT # Start moving to the LEFT
if self.y+self.height > self.parent.height:
self.direction = self.direction ^ UP # Remove the UP indicator
self.direction = self.direction ^ DOWN # Start moving DOWN
if self.y < 0:
self.direction = self.direction ^ DOWN
self.direction = self.direction ^ UP
if self.x < 0:
self.direction = self.direction ^ LEFT
self.direction = self.direction ^ RIGHT
def render(self):
self.draw()
# This class just sets up the window,
# self.heart <-- The important bit
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.x, self.y = 0, 0
self.heart = heart(self, x=100, y=100)
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.heart.update()
self.heart.render()
## Add stuff you want to render here.
## Preferably in the form of a batch.
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main()
x.run()
The basic principle is the same, manipulating sprite.x to move it laterally, and sprite.y vertically. There's more optimizations to be done, for instance, updates should be scaled according to last render. This is done to avoid glitches if your graphics card can't keep up. It can get quite complicate quite fast, so I'll leave you with an example of how to calculate those movements.
Further more, you probably want to render a batch, not the sprite directly. Which would speed up rendering processes quite a lot for larger projects.
If you're unfamiliar with bitwise operations, a short description would be that it operates on a bit/binary level (4 == 0100 as an example), and doing XOR operations on the values of UP, DOWN, LEFT and RIGHT. We can add/remove directions by merging 0100 and 0001 resulting in 0101 as an example. We can then do binary AND (not like the traditional and operator) to determinate if a value contains a 1 on the third position (0100) by doing self.direction & 0100 which will result in 1 if it's True. It's a handy quick way of checking "states" if you will.
My solution uses the midpoint between two fixed Sprites to determine whether the moving Sprite should go up or down. For this I made all letters individual Sprites, one png for each letter.
Hopefully this image will explain the code below a bit better.
#!/usr/bin/env python
import pyglet
CURR_BOUNCE = 0
MIDPOINTS = []
ENDPOINTS = []
def calculate_midpoint(s1, s2):
""" Calculate the midpoint between two sprites on the x axis. """
return (s1.x + s2.x) // 2
def should_move_down():
""" Decides if the Sprite is going up or down. """
global CURR_BOUNCE
# If the Sprite completed all bounces the app closes (not the best solution).
if max(len(MIDPOINTS), len(ENDPOINTS), CURR_BOUNCE) == CURR_BOUNCE:
raise SystemExit
# Move down if the Sprite is between the midpoint and the end (landing) point.
if MIDPOINTS[CURR_BOUNCE] <= heart_sprite.x <= ENDPOINTS[CURR_BOUNCE]:
return True
# If the Sprite has passed both the mid and end point then it can move on to the next bounce.
if max(MIDPOINTS[CURR_BOUNCE], heart_sprite.x, ENDPOINTS[CURR_BOUNCE]) == heart_sprite.x:
CURR_BOUNCE += 1
# Default behaviour is to keep going up.
return False
def update(dt):
"""
Move Sprite by number of pixels in each tick.
The Sprite always moves to the right on the x-axis.
The default movement on the y-axis is up.
"""
heart_sprite.x += dt * heart_sprite.dx
if should_move_down():
# To go down simply make the movement on the y-axis negative.
heart_sprite.y -= dt * heart_sprite.dy
else:
heart_sprite.y += dt * heart_sprite.dy
def center_image(image):
""" Sets an image's anchor point to its centre """
image.anchor_x = image.width // 2
image.anchor_y = image.height // 2
# Make window.
window = pyglet.window.Window(width=640, height=480)
# Set image path.
pyglet.resource.path = ['images']
pyglet.resource.reindex()
# Load images.
heart_img = pyglet.resource.image('red-heart.png')
cupcake_img = pyglet.resource.image('cupcake.png')
s_img = pyglet.resource.image('S.png')
# Add all letters here ...
t_img = pyglet.resource.image('t.png')
# Center images.
center_image(heart_img)
center_image(cupcake_img)
center_image(s_img)
# Centre all letters here ...
center_image(t_img)
# Make sprites.
half_window_height = window.height // 2
heart_sprite = pyglet.sprite.Sprite(img=heart_img, x=100, y=300)
# Set Sprite's speed.
heart_sprite.dx = 200
heart_sprite.dy = 90
cupcake_sprite = pyglet.sprite.Sprite(img=cupcake_img, x=550, y=half_window_height)
s_sprite = pyglet.sprite.Sprite(img=s_img, x=100, y=half_window_height)
# Make all letters into Sprites and adjust the x-axis coordinates...
t_sprite = pyglet.sprite.Sprite(img=t_img, x=310, y=half_window_height)
# Calculate midpoints.
# Here the midpoint between the 'bouncing point' and the 'landing point' is calculated.
# This is done for all bounces the Sprite makes.
MIDPOINTS.append(calculate_midpoint(s_sprite, t_sprite))
MIDPOINTS.append(calculate_midpoint(t_sprite, cupcake_sprite))
# The 'landing point' must be saved to be able to determine when one bounce has finished
# and move on to the next.
ENDPOINTS.append(t_sprite.x)
ENDPOINTS.append(cupcake_sprite.x)
# Rescale sprites.
heart_sprite.update(scale=0.05)
cupcake_sprite.update(scale=0.1)
s_sprite.update(scale=0.3)
# Resize all letter Sprites here ...
t_sprite.update(scale=0.3)
#window.event
def on_draw():
window.clear()
cupcake_sprite.draw()
heart_sprite.draw()
s_sprite.draw()
# Draw all letter Sprites here ...
t_sprite.draw()
#window.event
def on_mouse_press(x, y, button, modifiers):
"""
I only put the schedule_interval inside a mouse_press event so that I can control
when the animation begins by clicking on it. Otherwise the last line in this method
can be placed directly above the 'pyglet.app.run()' line. This would run the
animation automatically when the app starts.
"""
# Call update 60 times a second
pyglet.clock.schedule_interval(update, 1/60.)
if __name__ == '__main__':
pyglet.app.run()

rotate animation board (python, pygame)

This is more of a conceptual question:
If I want to rotate an entire playing board (a chess board for example) by 90 degrees, am I better off rotating the individual fields of the board around its center point individually, or is there a way for me to take a 'screenshot' of the field and rotate that as a single picture? I would have the code rotate the actual values of the board separately from the animation (once the animation was done, I would just redraw the screen with everything now in the right positions).
I hope my question is understandable. I have just started programming in python a couple of days ago and have so far only done text based games, but want to move onto GUI based games,
thank you in advance, your help is very much appreciated,
keelboat
You should rotate the board completely.
Taking a screenshot, and rotating that would be almost as taxing as just rotating the objects of the chessboard. Batching the objects and rotate them would be a solution.
Edit: i just realized that Pygame is one of the few libraries that might miss batched rendering. Pygame is nice, for a started learning curve but you're better off with other libraries (this is just a friendly suggestion)
Friendly suggestion
I'd go with Pyglet if I really wanted to do some cool stuff (including game development of your scale).
It's cross-platform, doesn't depend on Python version in the sense that all the others do and you get a direct hook to the OpenGL library making it redicilously fast. And it's quite easy to use actually.
Here's and example of drag and drop:
#!/usr/bin/python
import pyglet
from time import time, sleep
class Window(pyglet.window.Window):
def __init__(self, refreshrate):
super(Window, self).__init__(vsync = False)
self.frames = 0
self.framerate = pyglet.text.Label(text='Unknown', font_name='Verdana', font_size=8, x=10, y=10, color=(255,255,255,255))
self.last = time()
self.alive = 1
self.refreshrate = refreshrate
self.click = None
self.drag = False
def on_draw(self):
self.render()
def on_mouse_press(self, x, y, button, modifiers):
self.click = x,y
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
if self.click:
self.drag = True
print 'Drag offset:',(dx,dy)
def on_mouse_release(self, x, y, button, modifiers):
if not self.drag and self.click:
print 'You clicked here', self.click, 'Relese point:',(x,y)
else:
print 'You draged from', self.click, 'to:',(x,y)
self.click = None
self.drag = False
def render(self):
self.clear()
if time() - self.last >= 1:
self.framerate.text = str(self.frames)
self.frames = 0
self.last = time()
else:
self.frames += 1
self.framerate.draw()
self.flip()
def on_close(self):
self.alive = 0
def run(self):
while self.alive:
self.render()
# ----> Note: <----
# Without self.dispatc_events() the screen will freeze
# due to the fact that i don't call pyglet.app.run(),
# because i like to have the control when and what locks
# the application, since pyglet.app.run() is a locking call.
event = self.dispatch_events()
sleep(1.0/self.refreshrate)
win = Window(23) # set the fps
win.run()
Pyglet also enables you to do batched rendering (meaning you can send instructions to the GPU in a large chunk instead of item-per-item which makes it easy to make complex tasks quick and painless.. also you can do batch.rotate(90) and you're all done)
In Pygame you have a surface that is created when you initialize and configure your display. Often, people will blit other images directly to this surface, then update the display to render the image to the screen, but there's no reason why you can't create another surface to draw to, which can then be rotated and drawn to the surface rendered by the display.
screen = pygame.display.set_mode((500,500))
middle_man = pygame.Surface((500,500))
# draw stuff to middle_man here
....
# rotate middle_man
# note that this creates a padded surface if not rotating in 90 degree increments
rotated_middle_man = pygame.transform.rotate(middle_man, 45.0)
# draw middle_man back to the screen surface
crop_rect = pygame.Rect(rotated_middle_man.get_width() / 2 - screen.get_width() / 2,
rotated_middle_man.get_height() / 2 - screen.get_height() / 2,
screen.get_width(), screen.get_height())
screen.blit(rotated_middle_man, (0,0), crop_rect)
# update the screen
pygame.display.update()

Categories