Been at this for the past few hours, trying to make a small program where an image chases the cursor around. So far I've managed to make it so that the image is directly on top of the cursor and follows it around that way. However what I need is for the image to actually "chase" the cursor, so it would need to initially be away from it then run after it until it's then on top of the mouse.
Basically hit a wall with whats going wrong and what to fix up, here's what I've gotten so far:
from __future__ import division
import pygame
import sys
import math
from pygame.locals import *
class Cat(object):
def __init__(self):
self.image = pygame.image.load('ball.png')
self.x = 1
self.y = 1
def draw(self, surface):
mosx = 0
mosy = 0
x,y = pygame.mouse.get_pos()
mosx = (x - self.x)
mosy = (y - self.y)
self.x = 0.9*self.x + mosx
self.y = 0.9*self.y + mosy
surface.blit(self.image, (self.x, self.y))
pygame.display.update()
pygame.init()
screen = pygame.display.set_mode((800,600))
cat = Cat()
Clock = pygame.time.Clock()
running = True
while running:
screen.fill((255,255,255))
cat.draw(screen)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Clock.tick(40)
Probably not in the best shape of coding, been messing with this for just over 5 hours now. Any help is much appreciated! Thanks :)
Assuming you want the cat to move at a fixed speed, like X pixels per tick, you need to pick a new position X pixels toward the mouse cursor. (If you instead want the cat to move slower the closer it gets, you'd instead pick a position a certain % of the way between the current position and the mouse cursor. If you want it to move faster the closer it gets, you need to divide instead of multiply. And so on. But let's stick with the simple one first.)
Now, how do you move X pixels toward the mouse cursor? The usual way of describing this is: You find the unit vector in the direction from the current position to the cursor, then multiply it by X, and that gives you the steps to add. And you can reduce that to nothing fancier than a square root:
# Vector from me to cursor
dx = cursor_x - me_x
dy = cursor_y - me_y
# Unit vector in the same direction
distance = math.sqrt(dx*dx + dy*dy)
dx /= distance
dy /= distance
# speed-pixel vector in the same direction
dx *= speed
dy *= speed
# And now we move:
me_x += dx
me_y += dy
Note that me_x and me_y are going to be floating-point numbers, not integers. That's a good thing; when you move 2 pixels northeast per step, that's 1.414 pixels north and 1.414 pixels east. If you round that down to 1 pixel each step, you're going to end up moving 41% slower when going diagonally than when going vertically, which would look kind of silly.
I want to define a function to set the speed to characters in my game, I understand there is a formula for this:
rate * time = distance
1. Establish a rate of movement in whatever units of measure you want (such as pixels per millisecond).
2. Get the time since the last update that has passed (elapsed time).
3. Establish the direction of movement .
I have tryied to define a method which implements this:
def speed(self, speed):
clock = pygame.time.Clock()
milliseconds = clock.tick(60) # milliseconds passed since last frame
seconds = milliseconds / 1000.0
speed = seconds * (self.dx+self.dy)
But when I call this method to change the speed of my character, nothing happens.
Any suggestions?
You need to use the self keyword so set a class attribute in a method. But in your method you use speed as a parameter which I don't believe is how you want to use it.
def speed(self, speed):
clock = pygame.time.Clock()
milliseconds = clock.tick(60) # milliseconds passed since last frame
seconds = milliseconds / 1000.0
speed = seconds * (self.dx+self.dy)
This method takes a speed and then sets it equal to something and then it goes out of of scope with out making a change. To set an objects speed attribute with a method you would:
def set_speed(self):
clock = pygame.time.Clock()
milliseconds = clock.tick(60) # milliseconds passed since last frame
seconds = milliseconds / 1000.0
self.speed = seconds * (self.dx+self.dy)
self is the way to reference an object from inside one of it's methods. self.speed = 10 equates to doing my_object.speed = 10 outside the method. Using this method:
class Character:
def __init__():
self.speed = 0 # your character class needs to have a speed attribute somewhere
hero = Character() # creates an object that represents your character
hero.set_speed() # call the method to set the speed attribute of the character class
I don't know exactly how you have everything set up, but here is a rough example using 2-tuples (x, y) for position and speed (I think you might have self.x, self.y for my self.position and self.dx, self.dy for my self.speed, but the principles are the same):
def Character(object):
def __init__(self, position):
self.position = position # e.g. (200, 100)
self.speed = (0, 0) # start stationary
def move(self, elapsed):
"""Update Character position based on speed and elapsed time."""
# distance = time * rate
# position = old position + distance
self.position = (int(self.position[0] + (elapsed * self.speed[0])),
int(self.position[1] + (elapsed * self.speed[1])))
Note that you don't need to work out how far a Character should travel for a given speed when you set self.speed, just when you try to move the Character. You can directly access character.speed; having a "setter" method (e.g. set_speed(self, speed)) is unpythonic.
Now you could call this:
hero = Character(start_loc)
framerate = 60
clock = pygame.time.Clock()
...
while True: # main gameplay loop
elapsed = clock.tick(60) # update world time and get elapsed
...
hero.speed = (2, 0) # travelling horizontally in the positive x-direction
hero.move(elapsed) # update the hero's position
Note that speed here would be in units of pixels per millisecond.
I'm making a pygame game designed so that bullets will shoot in the direction of the mouse. I'm using
a Class to define the bullets in a list like this:
class Bullet:
def __init__(self,pos,speed,size):
self.pos = pos
self.speed = speed
self.size = size
def move(self):
self.pos[0] = int(self.pos[0] + self.speed[0])
self.pos[1] = int(self.pos[1] + self.speed[1])
I'm using this trigonometry function to get the vector of the angle in which I'm going to be shooting bullets.
def getUnitVector(x1, y1, x2, y2):
delx = x2 - x1
dely = y2 - y1
m = math.sqrt(delx * delx + dely * dely)
unit = (delx / m, dely / m)
return unit
level = [
I'm not using angles because I have to work around a pygame rounding error.
these are the variables I'm plugging into the function.
mousex, mousey = pygame.mouse.get_pos()
startx = 50
starty = 400
aim = getUnitVector(startx, starty, mousex, mouse
This how i'm handling the aim and making the bullets shoot from the start x,y
if pygame.mouse.get_pressed()[0]:
if reload>10:
bx = BULLETSPEED * aim[0]
by = BULLETSPEED * aim[1]
bullet = Bullet([startx,starty], [bx,by],10)
bullets.append(bullet)
reload=0
reload = reload + 1
I just want to let you know. I'm working on a school assignment and I will be learning more in depth about vectors and trig next unit so I don't really want to spend too much time learning this stuff right now :L . Also if you know any active python forums that might be more helpful in answer this question please comment. I cant find any.
Thank you for your time.
I might just build a work-around by only allowing it to shoot if the mouse is within 20 pixels or something so the error is minimized.
The bullet class should have a rectangle attribute so that the bullet's direction of travel runs through the bullet's center.
Doing this would first require reading the Pygame documentation here: Pygame.Rect Docs
Centering the bullet rectangle on the direction of travel could be implemented in a movement method as such:
def move(self):
self.dir = self.get_direction(self.target) # get direction
if self.dir: # if there is a direction to move
self.trueX += (self.dir[0] * self.speed) # calculate speed from direction to move and speed constant
self.trueY += (self.dir[1] * self.speed)
self.rect.center = (round(self.trueX),round(self.trueY)) # apply values to bullet.rect.center
More information can be found in this helpful sprite movement example:Pygame Sprite Movement
I am trying to write simple pendulum simulation in Pygame. The point is that I am trying to simulate directly the forces on the pendulum (gravity and tension) rather than solving the differential equation that describes the motion. First I wrote a function that get a vector, rotate the axis-system by some angle, and return this vector's components in the new, rotated axis-system; the code of this function is fine and it works as expected.
Each tick of the simulation I rotate the gravity vector by the angle between the pendulum and the rope, and get the new components - one in the direction of the rope, and one is orthogonal to it. the tension the and component in the direction of the rope cancelling each other, so only the orthogonal component is important. After I calculate it, I rotate the acceleration vector back to the normal coordinates system, and integrate. However, the resulting behavior is not as intended. What can be the reason?
This is the code:
from __future__ import division
import copy
import pygame
import random
import math
import numpy as np
import time
clock = pygame.time.Clock()
pygame.init()
size = (width, height) = (600,500)
screen = pygame.display.set_mode(size)
def rotate(vector,theta):
#rotate the vector by theta radians around the x-axis
Vx,Vy = vector[0],vector[1]
cos,sin = math.cos(theta),math.sin(theta)
newX,newY = Vx*cos-Vy*sin, Vy*cos+Vx*sin #the newX axis is the result of rotating x axis by theta
return [newX,newY]
class pendulum:
def __init__(self,x,y,x0,y0):
self.x = x
self.y = y
self.x0 = x0
self.y0 = y0
self.velocity = [0,0]
self.a= [0,0]
self.angle = 0
def CalcForce(self):
self.angle = math.atan2(-(self.y-self.y0),self.x-self.x0)
gravity = rotate(g,self.angle)
self.a[1]=gravity[1]
self.a[0] = 0 #This component is cancelled by the tension
self.a = rotate(self.a,-self.angle)
def move(self):
#print pylab.dot(self.velocity,[self.x-self.x0,self.y-self.y0])
self.velocity[0]+=self.a[0]
self.velocity[1]+=self.a[1]
self.x+=self.velocity[0]
self.y+=self.velocity[1]
def draw(self):
pygame.draw.circle(screen, (0,0,0), (self.x0,self.y0), 5)
pygame.draw.line(screen, (0,0,0), (self.x0,self.y0), (int(self.x), int(self.y)),3)
pygame.draw.circle(screen, (0,0,255), (int(self.x),int(self.y)), 14,0)
g = [0,0.4]
p = pendulum(350,100,300,20)
while 1:
screen.fill((255,255,255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
p.CalcForce()
p.move()
p.draw()
clock.tick(60)
pygame.display.flip()
Thank you.
There are a bunch of problems here. I'll fix a few and leave a few for you.
What I fixed was: 1) since you've imported numpy, you should use it, and write things in terms of the vectors; 2) it's an unreasonable demand on yourself to write everything and have it work immediately; so you need to plot intermediate results, etc, like here I plot a as well, so you can see whether it makes sense; 3) your whole "rotation" approach is confusing; instead think of component parts; which I calculate here directly (it's shorter, easier to read and understand, etc); 4) in all simulations where you use a time step, you should explicitly use dt so you can change the timestep without changing other parameters.
Now if you watch it you can see it looks almost reasonable. Notice though that the acceleration never goes upward, so the ball just falls while it oscillates. The reason for this is that you did not include the tension of the rope into the forces on the ball. I'll leave that part to you.
import pygame
import math
import numpy as np
clock = pygame.time.Clock()
pygame.init()
size = (width, height) = (600,500)
screen = pygame.display.set_mode(size)
class pendulum:
def __init__(self,x,y,x0,y0):
self.x0 = np.array((x0, y0))
self.x = np.array((x, y), dtype=float)
self.v = np.zeros((2,), dtype=float)
self.a = np.zeros((2,), dtype=float)
def CalcForce(self):
dx = self.x0 - self.x
angle = math.atan2(-dx[0], dx[1])
a = g[1]*math.sin(angle) # tangential accelation due to gravity
self.a[0] = at*math.cos(angle)
self.a[1] = at*math.sin(angle)
def move(self):
#print np.dot(self.a, self.x-self.x0) #is a perp to string?
self.x += dt*self.v
self.v += dt*self.a
def draw(self):
pygame.draw.circle(screen, (0,0,0), self.x0, 5)
pygame.draw.line(screen, (0,0,0), self.x0, self.x.astype(int),3)
pygame.draw.circle(screen, (0,0,255), self.x.astype(int), 14,0)
pygame.draw.line(screen, (255, 0, 0), (self.x+200*self.a).astype(int), self.x.astype(int), 4)
dt = .001
g = [0,0.4]
p = pendulum(350,100,300,20)
while 1:
screen.fill((255,255,255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
for i in range(100): # don't plot every timestep
p.CalcForce()
p.move()
p.draw()
clock.tick(60)
pygame.display.flip()
If you want to do a simulation, I think you're doing it the hard way. I'd start with the equation of motion, see Equation 20, here. The dots mean differentiate with respect to time---so the equation is d^2/dt^2 \theta = ... Then you should implement a finite differences scheme in the time direction, and step through time. At each step (label with i), you can calculate the x and y coordinates of the bob, based on the length of the pendulum and \theta_i. Check out the wiki article on finite differences.
I'm making a top down car racing game and I want to make the car rotate when you press the left and right keys (I’ve already done that part), the sprite's rotation is stored in a variable as degrees. I'd like to be able to make it move according to acceleration in the direction it is facing. I can figure out the acceleration part myself, it's just figuring out what pixel exactly is in that direction. Can anyone give me some simple code to help with this?
Here are the contents of the class that are relevant:
def __init__(self, groups):
super(Car, self).__init__(groups)
self.originalImage = pygame.image.load(os.path.join("Data", "Images", "Car.png")) #TODO Make dynamic
self.originalImage.set_colorkey((0,255,0))
self.image = self.originalImage.copy() # The variable that is changed whenever the car is rotated.
self.originalRect = self.originalImage.get_rect() # This rect is ONLY for width and height, the x and y NEVER change from 0!
self.rect = self.originalRect.copy() # This is the rect used to represent the actual rect of the image, it is used for the x and y of the image that is blitted.
self.velocity = 0 # Current velocity in pixels per second
self.acceleration = 1 # Pixels per second (Also applies as so called deceleration AKA friction)
self.topSpeed = 30 # Max speed in pixels per second
self.rotation = 0 # In degrees
self.turnRate = 5 # In degrees per second
self.moving = 0 # If 1: moving forward, if 0: stopping, if -1: moving backward
self.centerRect = None
def update(self, lastFrame):
if self.rotation >= 360: self.rotation = 0
elif self.rotation < 0: self.rotation += 360
if self.rotation > 0:
self.image = pygame.transform.rotate(self.originalImage.copy(), self.rotation)
self.rect.size = self.image.get_rect().size
self.center() # Attempt to center on the last used rect
if self.moving == 1:
self.velocity += self.acceleration #TODO make time based
if self.velocity > self.topSpeed: self.velocity = self.topSpeed # Cap the velocity
Trigonometry: The formula to get your coordinate is:
# cos and sin require radians
x = cos(radians) * offset
y = sin(radians) * offset
You use velocity for offset. (This means a negative velocity will drive backwards).
so:
def rad_to_offset(radians, offset): # insert better func name.
x = cos(radians) * offset
y = sin(radians) * offset
return [x, y]
loop_update is something like:
# vel += accel
# pos += rad_to_offset( self.rotation, vel )
math.cos, math.sin: uses radians, so
storing rotations as radians is simpler. If you want to define speed / etc as degrees, you still can.
# store radians, but define as degrees
car.rotation_accel = radians(45)
car.rotation_max_accel = radians(90)
I can't really do better than pointing you to this tutorial (*). In particular, the first part explains how to do rotation and make the sprites move in certain directions.
(*) Shameless plug :-) but very relevant to the question.