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

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()

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

How to dynamically instantiate objects from a certain class [duplicate]

This question already has answers here:
How do I create variable variables?
(17 answers)
Closed 2 years ago.
I'm making a simple ball game using a 2D map. The amount of balls on the map is variable. When starting the game I load the map and look for balls (The balls are unique letters). I append these ball names to a list.
I also have a Ball class. __init__ contains the coordinates of the ball and the class contains a couple of methods.
Here comes the problem:
I want to create objects of each ball using the Ball class, with the object name corresponding to the letter belonging to the ball.
I tried this:
(The map is a txt file looking like this:)
0 0 0 0
0 0 X 0
0 A 0 B
Where the zero's represent empty tiles.
class Ball:
def __init__( self, x, y):
self.x = x
self.y = y
map_data = np.genfromtxt('map.txt', dtype=str)
ball_names = []
for row in variables.map_data:
for i in row:
if i.isalpha():
ball_names.append(i)
for ball in ball_names:
coordinates = np.where(map_data == ball)
ball = Ball(coordinates[0], coordinates[1])
But then I want the object to be named after the string contained by the variable ball, not 'ball'.
I know this can be done using exec(), but I've been told to avoid that function.
Generating variables dynamically with exec is a bad idea. As in fact variables will be totally hidden from developer. Supporting such code will be a nightmare and it's going to be a giant source of problems.
Instead, why don't you create a dict of balls?
my_balls = dict()
for ball in ball_names:
coordinates = np.where(map_data == ball)
my_balls[ball] = Ball(coordinates[0], coordinates[1])
But if you really want to create a variable, then:
exec(f'{ball} = Ball({coordinates[0]}, {coordinates[1]})')

how to sense if a character is touching a colour

i am making a game and i need to be able to sense if a character is touching a tree or not and i can't because the tree is part of the background.
i've already tried using x and y co-ordinates but the trees aren't in a specific clump so this doesn't work. i also tried using a piece of code - look below - that detects if something is touching something else, however, the thing has to be defined and i can't define the tree.
def tree():
tree = #brown
if sprite_x < 0 + sprite_width and sprite_x + tree_width > 0 and sprite_y < 0 + sprite_height and sprite_height + sprite_y > tree_height :
#do stuff
i expect the code to sense if the character is touching a brown colour (the tree) and to allow me to use that output.

determining the first sprite to animate in python/pygame

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

Categories

Resources