determining the first sprite to animate in python/pygame - python

so I have this spritesheet (4 sprites in a row and 3 in a coloumn) which I use to animate a character in a game I make. It animates just fine without a problem, like I want it to
the problem start to arise when I want to change the state from "dash" (running to the enemy) to "attack" (well, attack the enemy) it doesn't seem to play the attack sprite from the start (index 0)
I've used self._currentFrame = 3 on the set_state(self) function so that when the function changes it resets the frame to the third frame, which makes (self.currentFrame + 1) % 4 returns 0
but still, sometimes it doesn't do what I want, and start the animation at about index 2 or 3 (the end of animation). How do I make sure that my animation starts at index 0?
my updating code is as follows, if it helps
self.frameTime += dt
if self.fps is not -1:
while self.frameTime > 1.0 / self.fps:
self.frameTime -= 1.0 / self.fps
self.currentFrame = (self.currentFrame + 1) % 4
self.currentVFrame = (self.currentVFrame + 1) % 3

Have you tested to ensure the starting value actually is 3, before that logic is executed? I ask because your summary includes reference to self._currentFrame, when your code refers to self.currentFrame -- are you assigning to one variable and checking another?
Edit regarding the additional 'answer' posted by the question starter:
Please see https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work for instructions on how (and when) to accept an answer, as well as why it's advantageous for you to do so with each question you ask.

Thanks Andrew, but I've solved it. It turned out I just need to adjust the self.fps(the animation fps, not the screen) so that 1.0 / self.fps is smaller than self.frameTime
Thanks Andrew, is there any way I can give you reputation? or end this question?
edit:
why the currentFrame printed as 3?
The Actor class (the class that inherited from AnimatedGameObject) has a set_state(self, newState) function (which changes the self.currentFrame to 3) that changes the state, so that the update code above (as I've explained) returns 0 and the animation starts at the beginning of the frame
The problem is when the self.fps in the update(self, dt) function has a value of 4, the 1.0 / self.fps has a lesser value than the self.frameTime and as the effect, the self.currentFrame is not set to 0 at the beginning of the animation
So I actually just need to double the self.fps to 8 and the code works just like I want it to

Related

Optimising pygame

For my project in AH Computing I'm recreating my version of Nidhogg. Everything runs smoothly until the blood starts spraying. Im not too sure how i can make the code more efficient as i am still fairly new to python.
This is the class for the the spraying blood:
class bloodmaker():
def __init__(self,xv,yv,colour,x,y):
self.gravity = 2
self.air_resistance = 0.25
self.xv = xv
self.yv = yv
self.x = x
self.y = y
self.pastx = 0
self.pasty = 0
self.colour = colour
def move(self):
if self.y < 400:
self.pastx = self.x
self.pasty = self.y
#so that it doesnt curve backwards
if self.xv > 0:
self.xv -= self.air_resistance
self.yv += self.gravity
self.x += self.xv
self.y += self.yv
#so that the drawn line doesnt go over the edge
if self.y > 400:
self.y = 400
if self.colour is "o":
py.draw.line(screen, (255, 165, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
py.draw.line(screen, (255, 255, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
global bloodgrid
try:
#the bloodgrid are squares 5 pixels wide, covering the bottom section, so we we divide by 5 to find where to put the blood
bloodgrid[int(self.x/5)].insert(0,self.colour)
except:
pass
#deleting this object as it is no longer required
return True
[Here is an image of the blood spraying][1]
(excuse the incomplete sprite)
[1]: https://i.stack.imgur.com/hXiAa.png
Underneath there is a floor of blood that works using an array of stacks, which is added above code when it the blood reaches the floor.
bloodgrid = [[] for x in range(512)]
Here is the code for destroying the flying blood object and blitzing the blood floor to the screen.
def blood():
for i in range(len(bloodarray)):
killyourself = bloodarray[i].move()
if killyourself is True:
kill.append(i)
#appends to a kill list as if i popped here the list would get shorter while the for loop stays the same giving an out of index error
for i in range(len(kill)):
bloodarray.pop(kill[0]-i)
kill.pop(0)
#printing the entire bloodgrid
for i in range(512):
for ii in range(len(bloodgrid[i])):
try:
if bloodgrid[i][ii] is "o":
py.draw.rect(screen, (255, 165, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
else:
py.draw.rect(screen, (255, 255, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
except:
pass
I don't think it is possible to only update parts of the screen as the camera moves about and the blood on the floor moves too.
As more blood accumulates on the floor the game framerate starts to drop and it gets especially choppy when the blood sprays. Is there any ways I could make this more efficient? I don't want to hand in a choppy game but I really like how the blood looks. Thanks.
A couple of points that are hopefully helpful.
(This isn't directly related to speeding up your code, but...) Try to get away from using global variables. Just pass them into your functions. Several of the ones you posted are difficult to figure out because you are accessing variables that are outside the scope of the function.
First reason things are bogging down here: You are using insert() to insert new elements at the start of a list, and you are doing that inside of a loop where you call the move() function. Inserting anywhere into a list (except the end) is horribly slow for reasons beyond the scope of this post. You should consider putting a deque from collections in there, which can be inserted front or back efficiently. If that is daunting, you could also "do a little math" and insert at the end of your list and draw it backwards by counting down from the last element, which is also efficient.
Also in the same notion, as mentioned in the comments, poping things out of the middle of lists or arrays is also horribly slow. Figure out a different way or look at a different data structure. It is tough to give advice on this point because bloodarray is not defined in your code.
Basically: things are bogging down because you are choosing the wrong data structures. tweak a couple of them and you'll get better results.

trying to make a quick-stop feature in my pygame platformer

Basically I'm trying to make it so that when you hit the down key the players dx value goes to 0 so that they can basically stop on a dime thought this would be pretty simple but it is proving more difficult than expected when I try to call on it nothing happens
def Q_Stop(self):
self.dx==0
== is a comparison operator , you want to use assign opertaor = :
def Q_Stop(self):
self.dx = 0

one unit snake in pygame even though its longer [duplicate]

This question already has an answer here:
How do I get the snake to grow and chain the movement of the snake's body?
(1 answer)
Closed 1 year ago.
ok so i have been playing around with pygame and python and I have already build snake but that was a very weird system and i want to build like a pathfinder for it that can path find around obstacles (the snake body) to the apple but the movement of the body is wonky and i know that sounds stupid since i cant even make snake how am I gonna make a pathfinder but i have done it before so here goes:
the snake will look like this: snake = [[1,0],[0,0]]
the direction is just stored in a tuple: direction = (xmov = 1,ymov = 0)
time += 1
if time > 30:
time = 0
snakeindex = len(snake)-1
snakeindex will be one
while snakeindex > 0:
this activates once and as far as it know it works
snake[snakeindex] = snake[snakeindex-1]
snakeindex -= 1
the snake will end up like this: [[1,0],[1,0]]
but then here:
snake[0][0] += direction[0]
snake[0][1] += direction[1]
the snake will then look like this: [[2,0],[2,0]]
Python optimization is messy sometimes for mutable objects like lists,dictionaries etc. Your snake parts is just one part, and references to this part.
You shouldn't do thing like:
a = [1,2]
b = a
# Now you might think there's to arrays
# But it's one array, and b is just a reference to a.
# If you change b or a both changes.
a[0] = 9
print(a, b)
# This will print out [9,2] and [9,2]
Use .copy() to actually copy list:
snake[snakeindex] = snake[snakeindex-1].copy()

My code works, But how do I make this code run in a deterministic time?

The Problem:
Given 3 inputs Bounce, Ball drop height, and ball view height.
How do I calculate the number of times the observer can see the ball pass.
So my code gives correct output, but it takes a longer time as bounce approaches 1, how do I make it deterministic in nature.
def bouncingBall(h, bounce, window):
BounceFactor = bounce
BallDropHeight = h
ViewerHeight = window
BounceLeft = h
BallSeenTimes = 1
if bounce > 1 or bounce < 0 or window >= h or h ==0:
return -1
else:
while (BounceLeft > ViewerHeight):
BounceLeft = BounceLeft * BounceFactor
if (BounceLeft > ViewerHeight):
BallSeenTimes = BallSeenTimes + 2
else:
break
return BallSeenTimes
I am not looking for code answers, just the direction in which I need to think.
Currently, I think If I could somehow establish a linear relationship and create a function to "guess" the passes it would be faster.
Not sure how much this is right. But,
var heightDiff = (BallDropHeight - ViewerHeight);
var reducedHeight = BallDropHeight *(1 - BounceFactor);
var BallSeenTimes = ((heightDiff/reducedLength)*2)+1;
may actually solve it. You just need to find the height reduced on each bounce. Then dividing the height difference between viewer and ball drop point with the reducedHeight will give the number of times ball bounced above the viewer. After that, multiplying with 2 is for each bounce viewer will see it twice. And adding one is for the first time drop.
There is no loop involved in it. So, the time taken to execute will be small only. Hopefully, it helps.!

Having a certain piece of code run for a specified amount of time

I'm working on a galactica type of game using pygame and livewires. However, in this game, instead of enemy's, there are balloons that you fire at. Every 25 mouse clicks, I have the balloons move down a row using the dy property set to a value of 1. If a balloon reaches the bottom, the game is over. However, I'm having some trouble figuring out how to get this to run only for, say, 1 second, or 2 seconds. Because I don't have a way to "time" the results, the dy value just indefinitely gets set to 1. Therefore, after the first 25 clicks, the row just keeps moving down. This is ok, but like I said, it's not my intended result.
Here is the code I have so far for this action:
if games.mouse.is_pressed(0):
new_missile = missile(self.left + 6, self.top)
games.screen.add(new_missile)
MISSILE_WAIT = 0 #25
CLICKS += 1
if CLICKS == 25:
SPEED = 1
CLICKS = 0
CLICKS, and MISSILE_WAIT are global variables that are created and set to an initial value of 0 before this block of code. What I'm trying to figure out is the algorithim to put underneath the if CLICKS statement. I've looked through the python documentation on the time module, but just can't seem to find anything that would suit this purpose. Also, I don't think using a while loop would work here, because the computer checks those results instantly, while I need an actual timer.
I'm not sure if I got your question but what I can suggest is that:
class Foo():
def __init__(self):
self.start_time = time.time()
self.time_delay = 25 # seconds
def my_balloon_func(self):
if(time.time() - self.start_time) > self.time_delay:
self.start_time = time.time()
else:
# do something

Categories

Resources