Making an opponent object using a class not working properly; they don't display on the screen for some reason? - python

So a while ago I learned how to create a class for which had the purpose of creating an "opponent" which would basically fight the player and etc. It was a good tutorial and while I did learn how to create this type of class, I also got issues from the code itself when testing it out.
One of the reasons it didn't work properly was because of flipping my sprites horizontally; I have 2 sprite variables, 1 that loads the images and the other which is supposed to contain a list. A loop then "flips" all the images from the original and stores it inside the empty list. This, however, caused an issue and started to make my sprite "flash" on the screen in both directions so I removed the loop, tried again and it worked but this time it only had one sprite(facing the left).
I also tried to remove the variables and the loop outside the class but that ended up not displaying the image at all.
#Goku Black
walkLeftGB = [ pygame.image.load("GB1.png"), pygame.image.load("GB2.png"), pygame.image.load("GB3.png"), pygame.image.load("GB4.png") ]
walkRightGB = []
for l in walkLeftGB:
walkRightGB.append(pygame.transform.flip(l, True, False))
for x in range(len(walkLeftGB)):
walkLeftGB[x] = pygame.transform.smoothscale(walkLeftGB[x], (372, 493))
for x in range(len(walkRightGB)):
walkRightGB[x] = pygame.transform.smoothscale(walkRightGB[x], (372, 493))
# === CLASSES === (CamelCase names)
class Enemy(object):
global vel
global walkCount1
global walkRightGB, walkLeftGB
def __init__(self, x, y, width, height, end):
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x, self.end]
self.walkCount1 = 0
self.vel = 3
self.walkLeftGB = walkLeftGB
self.walkRightGB = walkRightGB
def draw(self, DS):
self.move()
global walkCount1
if self.walkCount1 + 1 <= 33:
self.walkCount1 = 0
if self.vel > 0:
DS.blit(self.walkRightGB[self.walkCount1 //4], (self.x, self.y))
self.walkCount1 += 1
else:
DS.blit(self.walkLeftGB[self.walkCount1 //4], (self.x, self.y))
self.walkCount1 += 1
def move(self):
global walkCount1
if self.vel > 0:
if self.x + self.vel < self.path[1]:
self.x += self.vel
else:
self.vel = self.vel * -1
self.walkCount1 = 0
else:
if self.x - self.vel > self.path[0]:
self.x += self.vel
else:
self.vel = self.vel * -1
self.walkCount1 = 0
man = Enemy(1600, 407, 96, 136, 22)
def redrawGameWindow():
global walkCount
pygame.display.update()
man.draw(DS)
DS.blit(canyon,(0,0))
lastMoved = "left"
if walkCount + 1 >= 27:
walkCount = 0
if left:
DS.blit(walkLeft[walkCount//3],(x,y))
walkCount +=1
lastMoved = "left"
elif right:
DS.blit(walkRight[walkCount//3], (x,y))
walkCount +=1
lastMoved = "right"
else: #this is when its moving neither left or right
if lastMoved == "left":
DS.blit(char2, (x, y))
else:
DS.blit(char, (x, y))
#The "redrawGameWindow" is then called in a loop inside a function.
After I made those changes mentioned above, the output received was now the image not appearing at all, I expected the output for the image to appear(and maybe move)

This does not appear to do what you want it to:
if self.walkCount1 + 1 <= 33:
self.walkCount1 = 0
As a detail, it's weird to add one and compare to 33, when if self.walkCount1 <= 32 would suffice.
More importantly, it looks like you wanted >= there.
You have some hard-coded magic number divisors: // 3 and // 4.
Rather than e.g. 4, it would be much better to
refer to len(self.walkRightGB).
Then you could choose to insert new interpolated walking images,
or delete some, without having to worry about correctness of
other code that may be affected.
Numbers like 27 should also be expressed in more meaningful terms.
It's not clear to me you want // integer division.
Possibly you were looking for % modulo instead.
As written, it looks like there's a danger of the code
trying to access past the end of an image array.
Printing out the counts would help you to debug this,
for example by verifying that incremented count is preserved
across function calls.

Related

How to add snake body in pygame [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 2 years ago.
I'm a fairly newe programmer and this is the first time I develop a game and I wanted to start with something pretty simple, so I chose the snake game. I have coded everything apart from adding the body part when the food is eaten.
import random
import pygame
from pygame import *
import sys
import os
import time
###objects
class snake:
def __init__(self, win):
self.score = 1
self.length = 25
self.width = 25
self.win = win
self.r = random.randint(0,500)
self.vel = 25
self.update = pygame.display.update()
self.right = True
self.left = False
self.up = False
self.down = False
# 0 = right 1 = left 2 = up 3 = down
self.can = [True, False, True, True]
self.keys = pygame.key.get_pressed()
while True:
if self.r % 25 == 0:
break
else:
self.r = random.randint(0,500)
continue
self.x = self.r
self.y = self.r
self.r = random.randint(0,500)
while True:
if self.r % 25 == 0:
break
else:
self.r = random.randint(0,500)
continue
self.a = self.r
self.b = self.r
def move(self, win):
win.fill((0,0,0))
self.keys = pygame.key.get_pressed()
if self.right == True:
self.x += self.vel
if self.left == True:
self.x -= self.vel
if self.up == True:
self.y -= self.vel
if self.down == True:
self.y += self.vel
if self.x > 475:
self.x = 0
if self.x < 0:
self.x = 500
if self.y > 475:
self.y = 0
if self.y < 0:
self.y = 500
if self.keys[pygame.K_RIGHT] and self.can[0] == True:
self.right = True
self.left= False
self.up = False
self.down = False
self.can[1] = False
self.can[0] = True
self.can[2] = True
self.can[3] = True
if self.keys[pygame.K_LEFT] and self.can[1] == True:
self.right = False
self.left = True
self.up = False
self.down = False
self.can[0] = False
self.can[1] = True
self.can[2] = True
self.can[3] = True
if self.keys[pygame.K_UP] and self.can[2] == True:
self.right = False
self.left = False
self.up = True
self.down = False
self.can[3] = False
self.can[0] = True
self.can[1] = True
self.can[2] = True
if self.keys[pygame.K_DOWN] and self.can[3] == True:
self.right = False
self.left = False
self.up = False
self.down = True
self.can[2] = False
self.can[0] = True
self.can[1] = True
self.can[3] = True
self.length = 25 * self.score
self.snake = pygame.draw.rect(self.win, (0,255,0), (self.x, self.y, self.length, self.width))
def food(self, win):
pygame.draw.rect(self.win, (255,0,0), (self.a, self.b,25,25))
if self.a == self.x and self.b == self.y:
self.r = random.randint(0,500)
while True:
if self.r % 25 == 0:
break
else:
self.r = random.randint(0,500)
continue
self.a = self.r
self.b = self.r
self.score += 1
###functions
###main game
##variables
screen = (500,500)
W = 25
L = 25
WHITE = 255,255,255
clock = pygame.time.Clock()
##game
pygame.init()
win = pygame.display.set_mode(screen)
title = pygame.display.set_caption("snake game")
update = pygame.display.update()
snake = snake(win)
run = True
while run:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
snake.move(win)
snake.food(win)
pygame.display.update()
pygame.quit()
I know the code is a bit messy because I wanted to try to implement OOP, since I never used it.
This is also my first time using pygame, so I be doing something wrong.
So far I have made it so that the snake and food spawn in a random location in an invible grid, and when the head of the snake has the same coordinates of the food, the snake becomes longer (I'm just adding 25 pixels to the snake's body, but when it turns, the whole rectangular shaped snake turns). Also, if the snake reaches the edge of the display, the appears from the opposite side.
The comments below might sound harsh, and I've tried to write them in a neutral way simply pointing out facts and state them as they are. If you are truly a new programmer, this is a pretty good project to learn from and you've done quite good to come this far. So keep an mind that these comments are not meant to be mean, but objective and always comes with a proposed solution to make you an even better programmer, not to bash you.
I also won't go into detail in the whole list as a body thing, others have covered it but I'll use it also in this code.
Here's the result, and blow is the code and a bunch of pointers and tips.
Never re-use variables
First of all, never re-use variable names, as you've overwritten and got lucky with snake = snake() which replaces the whole snake class, and can thus never be re-used again, defeating the whole purpose of OOP and classes. But since you only use it once, it accidentally worked out ok this time. Just keep that in mind for future projects.
Single letter variables
Secondly, I would strongly avoid using single-letter variables unless you really know what you're doing and often that's tied to a math equation or something. I'm quite allergic to the whole concept of self.a and self.b as they don't say anything meaningful, and in a few iterations you probably won't have an idea of what they do either. This is common tho when you're moving quickly and you currently have a grasp on your code - but will bite you in the ass sooner or later (will/should give you bad grades in school or won't land you that dream job you're applying for).
Never mix logic in one function
You've also bundled the food into the player object, which is a big no-no. As well as render logic in the movement logic. So I propose a re-work in the shape of even more OOP where food and player are two separate entities and a function for each logical operation (render, move, eat, etc..).
So I restructured it into this logic:
While I'm at it, I also re-worked the movement mechanics a bit, to use less lines and logic to produce the same thing. I also removed all this logic:
self.r = random.randint(0,500)
while True:
if self.r % 25 == 0:
break
else:
self.r = random.randint(0,500)
continue
And replaced it with this, which does the exact same thing, but uses built-ins to produce it. And hopefully the functions/variables are more descriptive than a rogue while loop.
self.r = random.choice(range(0, 500, 25))
And the final result would look something like this:
import random
import pygame
from pygame import *
import sys
import os
import time
# Constants (Used for bitwise operations - https://www.tutorialspoint.com/python/bitwise_operators_example.htm)
UP = 0b0001
DOWN = 0b0010
LEFT = 0b0100
RIGHT = 0b1000
###objects
class Food:
def __init__(self, window, x=None, y=None):
self.window = window
self.width = 25
self.height = 25
self.x, self.y = x, y
if not x or not y: self.new_position()
def draw(self):
pygame.draw.rect(self.window, (255,0,0), (self.x, self.y, 25, 25))
def new_position(self):
self.x, self.y = random.choice(range(0, 500, 25)), random.choice(range(0, 500, 25))
class Snake:
def __init__(self, window):
self.width = 25
self.width = 25
self.height = 25
self.window = window
self.vel = 25
self.update = pygame.display.update()
start_position = random.choice(range(0, 500, 25)), random.choice(range(0, 500, 25))
self.body = [start_position]
self.direction = RIGHT
def move(self, window):
self.keys = pygame.key.get_pressed()
# since key-presses are always 1 or 0, we can multiply each key with their respective value from the
# static map above, LEFT = 4 in binary, so if we multiply 4*1|0 we'll get binary 0100 if it's pressed.
# We can always safely combine 1, 2, 4 and 8 as they will never collide and thus always create a truth map of
# which direction in bitwise friendly representation.
if any((self.keys[pygame.K_UP], self.keys[pygame.K_DOWN], self.keys[pygame.K_LEFT], self.keys[pygame.K_RIGHT])):
self.direction = self.keys[pygame.K_UP]*1 + self.keys[pygame.K_DOWN]*2 + self.keys[pygame.K_LEFT]*4 + self.keys[pygame.K_RIGHT]*8
x, y = self.body[0] # Get the head position, which is always the first in the "history" aka body.
self.body.pop() # Remove the last object from history
# Use modolus to "loop around" when you hit 500 (or the max width/height desired)
# as it will wrap around to 0, try for instance 502 % 500 and it should return "2".
if self.direction & UP:
y = (y - self.vel)%500
elif self.direction & DOWN:
y = (y + self.vel)%500
elif self.direction & LEFT:
x = (x - self.vel)%500
elif self.direction & RIGHT:
x = (x + self.vel)%500 # window.width
self.body.insert(0, (x, y))
def eat(self, food):
x, y = self.body[0] # The head
if x >= food.x and x+self.width <= food.x+food.width:
if y >= food.y and y+self.height <= food.y+food.height:
self.body.append(self.body[-1])
return True
return False
def draw(self):
for x, y in self.body:
pygame.draw.rect(self.window, (0,255,0), (x, y, self.width, self.width))
##variables
clock = pygame.time.Clock()
##game
pygame.init()
window = pygame.display.set_mode((500,500))
pygame.display.set_caption("snake game")
snake = Snake(window)
food = Food(window)
food.new_position()
score = 0
run = True
while run:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill((0,0,0)) # Move the render logic OUTSIDE of the player object
snake.move(window)
if snake.eat(food):
score += 1
food.new_position()
snake.draw()
food.draw()
pygame.display.update()
pygame.quit()
draw() now handles all rendering logic within the objects themselves, instead of being entangled in the move().
snake.eat() is now a function that returns True or False based on the snake head (first position in history, aka body) being inside a food object. This function also adds to the body if a eat was successful, perhaps this code should be moved outside as well, but it's one line of code so I skipped on my own rule a bit to keep the code simple.
food.new_position() is a function that simply moves the food to a new position, called when eat() was successful for instance, or if you want to randomly move the food around at a given interval.
move() and finally the move function, which only has one purpose now, and that is to move the snake in a certain direction. It does so by first getting the current head position, then remove the last history item (tail moves with the head) and then adds a new position at the front of the body that is equal to velocity.
The "is inside" logic might look like porridge, but it's quite simple, and the logic is this:
If the snakes head body[0] has it's x greater or equal to the foods x, it means the heads lower upper left corner was at least past or equal to the foods upper left corner. If the heads width (x+width) is less or equal to the foods width, we're at least inside on the X axis. And then we just repeat for the Y axis and that will tell you if the head is inside or outside the boundary of the food.
The movement logic is reworked to make it fractionally faster but also less code and hopefully easier to use once you have a understanding of how it works. I switched to something called bitwise operations. The basic concept is that you can on a "machine level" (bits) do quick operations to determinate if something is true or not with AND operations for instance. To do this, you can compare to bit-sequences and see if at any point two 1 overlap each other, if not, it's False. Here's an overview of the logic used and all the possible combinations of UP, DOWN, LEFT and RIGHT in binary representation:
On a bit level, 1 is simply 0001, 2 would be 0010 and 4 being 0100 and finally 8 being 1000. Knowing this, if we press → (right) we want to convert this into the bit representation that is the static variable RIGHT (1000 in binary). To achieve this, we simply multiply the value pygame gives us when a key is pressed, which is 1. We multiply it by the decimal version of 1000 (RIGHT), which is 8.
So if → is pressed we do 8*1. Which gives us 1000. And we simply repeat this process for all the keys. If we pressed ↑ + → it would result in 1001 because 8*1 + 1*1 and since ← and ↓ weren't pressed, they will become 4*0 and 2*0 resulting in two zeroes at binary positions.
We can then use these binary representations by doing the AND operator shown in the picture above, to determinate if a certain direction was pressed or not, as DOWN will only be True if there's a 1 on the DOWN position, being the second number from the right in this case. Any other binary positional number will result in False in the AND comparitor.
This is quite efficient, and once you get the hang of it - it's pretty useful for other things as well. So it's a good time to learn it in a controlled environment where it hopefully makes sense.
The main thing to take away here (other than what other people have already pointed out, keep the tail in a array/list as a sort of history of positions) is that game objects should be individual objects, and main rendering logic shouldn't be in player objects, only player render specifics should be in the player object (as an example).
And actions such as eat() should be a thing rather than being checked inside the function that handles move(), render() and other things.
And my suggestions are just suggestions. I'm not a game developer by trade, just optimizing things where I can. Hope the concepts come to use or spark an idea or two. Best of luck.
Yo have to mange the body of the snake in a list. Add the current position of the the head at the head of the body list and remove an element at the tail of the list in ever frame.
Add an attribute self.body:
class snake:
def __init__(self, win):
# [...]
self.body = [] # list of body elements
Add the current head to the bode before the head is moved:
class snake:
# [...]
def move(self, win):
# [...]
# move snake
self.body.insert(0, (self.x, self.y))
Remove elements a the end of self.body, as long the length of the snake exceeds the score:
class snake:
# [...]
def move(self, win):
# [...]
# remove element at end
while len(self.body) >= self.score:
del self.body[-1]
Draw the bode of the snake in a loop:
class snake:
# [...]
def move(self, win):
# [...]
# draw smake and body
self.snake = pygame.draw.rect(self.win, (0,255,0), (self.x, self.y, 25, self.width))
for pos in self.body:
pygame.draw.rect(self.win, (0,255,0), (pos[0], pos[1], 25, self.width))
class snake:
class snake:
def __init__(self, win):
self.score = 1
self.length = 25
self.width = 25
self.win = win
self.r = random.randint(0,500)
self.vel = 25
self.update = pygame.display.update()
self.right = True
self.left = False
self.up = False
self.down = False
# 0 = right 1 = left 2 = up 3 = down
self.can = [True, False, True, True]
self.keys = pygame.key.get_pressed()
while True:
if self.r % 25 == 0:
break
else:
self.r = random.randint(0,500)
continue
self.x = self.r
self.y = self.r
self.body = [] # list of body elements
self.r = random.randint(0,500)
while True:
if self.r % 25 == 0:
break
else:
self.r = random.randint(0,500)
continue
self.a = self.r
self.b = self.r
def move(self, win):
win.fill((0,0,0))
self.keys = pygame.key.get_pressed()
# move snake
self.body.insert(0, (self.x, self.y))
if self.right == True:
self.x += self.vel
if self.left == True:
self.x -= self.vel
if self.up == True:
self.y -= self.vel
if self.down == True:
self.y += self.vel
if self.x > 475:
self.x = 0
if self.x < 0:
self.x = 500
if self.y > 475:
self.y = 0
if self.y < 0:
self.y = 500
# remove element at end
while len(self.body) >= self.score:
del self.body[-1]
if self.keys[pygame.K_RIGHT] and self.can[0] == True:
self.right = True
self.left= False
self.up = False
self.down = False
self.can[1] = False
self.can[0] = True
self.can[2] = True
self.can[3] = True
if self.keys[pygame.K_LEFT] and self.can[1] == True:
self.right = False
self.left = True
self.up = False
self.down = False
self.can[0] = False
self.can[1] = True
self.can[2] = True
self.can[3] = True
if self.keys[pygame.K_UP] and self.can[2] == True:
self.right = False
self.left = False
self.up = True
self.down = False
self.can[3] = False
self.can[0] = True
self.can[1] = True
self.can[2] = True
if self.keys[pygame.K_DOWN] and self.can[3] == True:
self.right = False
self.left = False
self.up = False
self.down = True
self.can[2] = False
self.can[0] = True
self.can[1] = True
self.can[3] = True
# draw smake and body
self.snake = pygame.draw.rect(self.win, (0,255,0), (self.x, self.y, 25, self.width))
for pos in self.body:
pygame.draw.rect(self.win, (0,255,0), (pos[0], pos[1], 25, self.width))
def food(self, win):
pygame.draw.rect(self.win, (255,0,0), (self.a, self.b,25,25))
if self.a == self.x and self.b == self.y:
self.r = random.randint(0,500)
while True:
if self.r % 25 == 0:
break
else:
self.r = random.randint(0,500)
continue
self.a = self.r
self.b = self.r
self.score += 1
I would make a body part object and when the snake gets longer you add a body part. The head does the movement and the body parts follow the head.
Each game turn you just move the head then go over all of the body parts starting from the one closest to the head and move them to their parents location. So head moves 1 block, next part moves the previous head location, third part moves to second parts previous location, ...

How do I implement random direction change on my pygame enemy?

So I currently have an enemy class that moves left, encounters the end of the screen, then flips direction 180 degrees and walks right. It does this constantly.
I wish to make it randomly change direction, so it's more unpredictable. The issue I'm encountering is when I implement a random number generator for the distance of direction, how would I then inherit it into a variable when I create the instance? Or is there a more efficient way to do this?
Entire enemy class:
class enemy(object):#need to use self to access these
walkRight = [pygame.image.load('Game/R1E.png'),pygame.image.load('Game/R2E.png'),pygame.image.load('Game/R3E.png'),pygame.image.load('Game/R4E.png'),pygame.image.load('Game/R5E.png'),pygame.image.load('Game/R6E.png'),pygame.image.load('Game/R7E.png'),pygame.image.load('Game/R8E.png'),pygame.image.load('Game/R9E.png'),pygame.image.load('Game/R10E.png'),pygame.image.load('Game/R11E.png')]
walkLeft = [pygame.image.load('Game/L1E.png'),pygame.image.load('Game/L2E.png'),pygame.image.load('Game/L3E.png'),pygame.image.load('Game/L4E.png'),pygame.image.load('Game/L5E.png'),pygame.image.load('Game/L6E.png'),pygame.image.load('Game/L7E.png'),pygame.image.load('Game/L8E.png'),pygame.image.load('Game/L9E.png'),pygame.image.load('Game/L10E.png'),pygame.image.load('Game/L11E.png')]
def __init__(self,x,y,width,height,end): #get R3E png
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x,self.end]
self.walkCount = 0
self.vel = 3
def draw(self,window): #enemy is gonna move from left, to right, left, to right etc between 2 co ordinate points
self.move()
if self.walkCount + 1 >= 33: #if sign is changed, as walkcount would always be less than 33, it wouldn't change
self.walkCount = 0
#rather than using self.left, sel.right, we can use out velocity
if self.vel > 0: #this means we're moving right, integer division 3 so it doesn't look like we going too fast
window.blit(self.walkRight[self.walkCount //3],(self.x,self.y))
self.walkCount += 1
else:
window.blit(self.walkLeft[self.walkCount //3],(self.x,self.y))
self.walkCount += 1 #if not moving right, we're moving left
#check if we're drawing an image to the left or right
def move(self): #move method #to change directions, he needs to change velocity (multiply by -1 etc)
if self.vel > 0:
if self.x < self.path[1] + self.vel:#check if he's about to move past the point on screen, we're accessing the self.end eleme
self.x += self.vel
else: #e.g. if it's greater than, we change direction
self.vel = self.vel * -1 #flipped 180 degrees, so goes other direction
self.x += self.vel
self.walkCount = 0
else: #if our vel is negative
if self.x > self.path[0] - self.vel:
self.x += self.vel #vel is gonna be negative already if we've changed directions
else:
self.vel = self.vel * -1
self.x += self.vel
self.walkCount = 0
Movement function:
def move(self): #move method #to change directions, he needs to change velocity (multiply by -1 etc)
if self.vel > 0:
if self.x < self.path[1] + self.vel:#check if he's about to move past the point on screen, we're accessing the self.end eleme
self.x += self.vel
else: #e.g. if it's greater than, we change direction
self.vel = self.vel * -1 #flipped 180 degrees, so goes other direction
self.x += self.vel
self.walkCount = 0
else: #if our vel is negative
if self.x > self.path[0] - self.vel:
self.x += self.vel #vel is gonna be negative already if we've changed directions
else:
self.vel = self.vel * -1
self.x += self.vel
self.walkCount = 0
Creating the instance just before the main loop(x,y,height,width,walk distance):
goblin = enemy(0,440,64,64,450)
My attempt - still only takes one random number & permanently uses it whilst the game runs.
def timerthing():
pathenemy = random.randint(0,450)
return pathenemy
#now to write main loop which checks for collisions, mouse events etc
#make path randomly change
man = player(200,444,64,66) #creates instance of the player / object
goblin = enemy(0,440,64,64,timerthing())#need to create instance of enemy so he appears on the screen, path ends at timerthing
run = True
bullets = []
while run:
clock.tick(15) #frame rate
timerthing()
Instead of a constant of 450, I'm after a constantly changing variable instead, so his movement is unpredictable. Any other suggestions would be appreciated - thanks.
import random
def timerthing():
pathenemy = random.randint(0,450)
time.sleep(1)
return pathenemy
But I suspect that's not really your problem. It sounds like you have a configuration problem rather than a source problem, and to solve that we'd need an entirely different kind of information from you.
If I had to do code this randomly moving enemy, I would implement two methods first:
One method that returns the possible directions my enemy can go through, given the state of the board. Yet I can't help you with that, since we don't have the "board" part of your code. Let's call this method getPossibleDirections()
One method move, with one argument direction, representing the direction taken by my enemy. Let's call this method move(direction)
Based on your code, I guess you can implement these two methods by yourself. And maybe you want your ennemy to move in the y direction too?
Like #Mark Storer said, you could use the random package, but I will use the random part of the numpy library (already included in python) for my answer:
# you will need to import numpy in the beginning of your file
import numpy as np
class enemy(...)
# your code goes there
def getPossibleDirections(self):
#your implementation
pass
def move(self, direction):
#your implementation
pass
def moveRandomly(self):
possibleDirections = self.getPossibleDirections()
chosenDirection = np.random.randint(0,len(possibleDirections))
# returns one number between 0 and len(possibleDirections) (not included) that can represent your direction
self.move(possibleDirection[chosenDirection])
To move your ennemy randomly, you will just have to call enemy.moveRandomly().

I cant figure out this space key and attack animation

I decided to add the attack images into the walking ones to see what happened and I got them to load up.
I have been attempting to break it down and piece it back together to try and find what I have been doing wrong and it may be an issue of my experience.
I have butchered my code trying to figure this out after someone gave me an explanation about my classes. I thought maybe I should try and set it up as global, and then I realize there's something I'm not doing right. I don't want someone to just write the code for me I should mention. But an explanation for why I am screwing up would be nice.
Some tutorials are helping me but not as much as when someone tells me where specifically where I went wrong. I think I made more progress there.
##a simple punch without the whole animation atm
class attacks(object):
attack = pygame.image.load("atk3.png")
def __init__(self, x, y, width, height, attack):
self.x = x
self.y = y
self.width = width
self.height = height
self.attackCount = 0
def draw(self, win):
## is it my redraw method?
if (self.attack):
if self.attackCount + 1 >= 9:
win.blit(attack, self.attackCount//3(self.x, self.y))
self.attackCount += 1
So with the class set up here im sure that something is wrong, ive been diving through forums and I know something is not right. I dont know what though.
def redrawGameWindow():
global walkCount
global attackCount
win.blit(bg, (0, 0))
if walkCount +1 >= 27:
walkCount = 0
bob.draw(win)
jerk.draw(win)
for attack in attacks:
attack.draw(win)
pygame.display.update()
#While true
#This calls the classes created
bob = player(100, 200, 128, 128)
jerk = enemy(500, 200, 164, 100, 600)
attacks = []
run = True
while run:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
#attempting to make this into a bool that changes the value
if not(bob.attacking):
if keys[pygame.K_SPACE]:
bob.attacking = True
bob.standing = False
bob.left = False
bob.right = False
if keys[pygame.K_LEFT] and bob.x > bob.vel:
bob.x -= bob.vel
bob.left = True
bob.right = False
bob.standing = False
elif keys[pygame.K_RIGHT] and bob.x < 800 - bob.width - bob.vel:
bob.x += bob.vel
bob.right = True
bob.left = False
bob.standing = False
else:
bob.standing = True
bob.walkCount = 0
if not(bob.isJump):
if keys[pygame.K_UP]:
bob.isJump = True
bob.right = False
bob.left = False
bob.walkCount = 0
else:
if bob.jumpCount >= -10:
neg = 1
if bob.jumpCount < 0:
neg = -1
bob.y -= (bob.jumpCount ** 2) * 0.5 * neg
bob.jumpCount -= 1
else:
bob.isJump = False
bob.jumpCount = 10
redrawGameWindow()
pygame.quit()
Im almost sure my True loop has nothing to do with it now since im able to run walk and jump. And when I press space it loads a frame thats not an attack one so i know for sure something is happening when I hit space
one of the errors I have found is that you are telling pygame to load multiple images however, you have only loaded one
class attacks(object):
attack = pygame.image.load("atk3.png")#< ----- you are only loading one
def __init__(self, x, y, width, height, attack):
#[...]
and you are telling it to go in a cycle to blit different images in a group
def draw(self, win):
## is it my redraw method?
if (self.attack):
if self.attackCount + 1 >= 9:
win.blit(attack, self.attackCount//3(self.x, self.y))# you only have one image yet you are trying to blit multiple
self.attackCount += 1
another error is your attack varible. to know when you luanch an attack you do it like this
class attacks(object):
attack = pygame.image.load("atk3.png")
def __init__(self, x, y, width, height,#attack <----- this attack shoudnt be here):
self.x = x
self.y = y
self.width = width
self.height = height
self.attackCount = 0
self.punchisluacnhed = false
def draw(self, win):
if (self.punchisluacnhed):
if self.attackCount + 1 >= 9:
win.blit(self.attack, self.attackCount//3(self.x, self.y))
self.attackCount += 1
#B.T.W the attack image in the draw function should have self before it since you called it in a class not in the initilization i.e import pygame etc
If you want the character to punch when a button is pressed you would put it in a function like this
if keys[pygame.K_SPACE]:
bob.punchisluacnhed = True
bob.standing = False
bob.left = False
bob.right = False
to tell it to stop you put an else stateent an set every thing to false except from the standing value

Sprite not moving in the same direction it is facing

I have an issue with my sprite not moving in the same direction as it is facing. It is top view of a beetle, 10 images animating its leg movements. The sprite animation works fine, choosing new 'random' direction by rotating the image and re-centering to previous center is working too.
What I can't get to work is the sprite to move 'forward', that is to move in the new direction it chooses/faces every second or so. The new direction is simply a small 10-15 degrees rotation left or right from previous position. Instead it moves in what seem like random movements every time the sprite chooses a new direction. For example it will move southwest while facing east, or move north while facing south etc.
I suspect the problem is in the move() method where the movement isn't properly translated via trig values. I am adding to the rect.x values because it is the same as the cartesian coords system while subtracting from rect.y values because it is inverted in pygame compared to cartesian. Going down is increasing positively, up - decreasing y values, increasing negatively.
The class for the beetle sprite is below:
class Foe():
def __init__(self, location):
self.sprites = []
for i in range(1, 11):
file = pg.image.load("beetleprac1/000"+str(i)+"a.png").convert() # 10 sprite files named "0001a-10a.png" last 1 is idle stance
#file.set_colorkey((0, 255, 255))
self.sprites.append(file)
self.move_anim_index = 9
self.image = self.sprites[self.move_anim_index]
self.rotated_image = self.image.copy()
self.rect = self.image.get_rect(center=location)
self.move_rect_coords = [self.rect.x, self.rect.y]
self.angle = 90
self.speed = 3
self.time = 0
self.frames_counter = 0
def move_anim(self, rate=2): # rate is in frames
if self.frames_counter % rate == 0:
self.move_anim_index += 1
if self.move_anim_index >= 9:
self.move_anim_index = 0
self.image = self.sprites[self.move_anim_index]
#self.rect = self.image.get_rect(center=self.rect.center)
self.rotated_image = self.image.copy()
self.rotated_image = pg.transform.rotate(self.image, self.angle - 90)
self.rect = self.rotated_image.get_rect(center=self.rect.center)
def move(self, last_tick):
self.move_anim()
self.rect.x += int(self.speed * math.cos(self.angle))
self.rect.y -= int(self.speed * math.sin(self.angle))
self.move_rect_coords = [self.rect.x, self.rect.y]
def change_direction(self, change_time): # change_time in frames
if self.frames_counter % change_time == 0:
a = random.choice([-1, 1])
self.angle += 15
if self.angle < 0: self.angle += 360
if self.angle > 360: self.angle -=360
#if self.rect.x <
def track_time(self, last_tick, amount):
self.time += last_tick
self.frames_counter += 1
if self.time >= last_tick * amount:
self.time = 0
self.counter = 0
def think(self, last_tick):
self.track_time(last_tick, FPS*2)
self.change_direction(FPS)
def update(self, last_tick, screen_rect):
self.think(last_tick)
self.move(last_tick)
def draw(self, screen):
screen.blit(self.rotated_image, self.move_rect_coords)
And here is the full code and repository at github: https://github.com/fn88/buganimprac2
sin(), cos() and other trigonometric functions use radians -> cos(math.radians(angle))

Method body not executing?

I'm running this python code and having a problem with the accel function. The rotate method works fine when left and right are pressed however when up is pressed nothing happens. I've stepped through the code in a debugger and the my_ship.accel line is executed but it doesn't go to method body, it just continues as if that line isn't there. Idk what's wrong please help. Also my_ship is the name of a Ship object and it is defined properly lower in my code.
import simplegui
WIDTH = 800
HEIGHT = 600
class ImageInfo:
def __init__(self, center, size, radius = 0, lifespan = None, animated = False):
self.center = center
self.size = size
self.radius = radius
if lifespan:
self.lifespan = lifespan
else:
self.lifespan = float('inf')
self.animated = animated
def get_center(self):
return self.center
def get_size(self):
return self.size
def get_radius(self):
return self.radius
def get_lifespan(self):
return self.lifespan
def get_animated(self):
return self.animated
def change_center(self, new_center):
self.center = new_center
# ship image
ship_info = ImageInfo([45, 45], [90, 90], 35)
ship_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png")
class Ship:
def __init__(self, pos, vel, angle, image, info):
self.pos = [pos[0],pos[1]]
self.vel = [vel[0],vel[1]]
self.thrust = False
self.angle = angle
self.angle_vel = 0
self.image = image
self.image_center = info.get_center()
self.image_size = info.get_size()
self.radius = info.get_radius()
self.info = info
self.accel = 10
self.angle_accel = .1
def draw(self,canvas):
if not self.thrust:
self.info.change_center(ship_center)
canvas.draw_image(self.image, self.image_center, self.image_size, self.pos, self.image_size, self.angle)
else:
self.info.change_center(thrust_ship_center)
canvas.draw_image(self.image, self.image_center, self.image_size, self.pos, self.image_size, self.angle)
def update(self):
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
self.angle += self.angle_vel
def accel(self):
self.thrust = True
self.vel[0] += self.accel
self.vel[1] += self.accel
def rotate(self, direction):
if direction == "left":
self.angle_vel -= self.angle_accel
elif direction == "right":
self.angle_vel += self.angle_accel
else:
print "error"
def keydown_handler(key):
if key == simplegui.KEY_MAP['left']:
my_ship.rotate("left")
elif key == simplegui.KEY_MAP['right']:
my_ship.rotate("right")
elif key == simplegui.KEY_MAP['up']:
my_ship.accel
elif key == simplegui.KEY_MAP['space']:
self.angle_vel += self.angle_accel
def keyup_handler(key):
if key == simplegui.KEY_MAP['left']:
my_ship.rotate("right")
elif key == simplegui.KEY_MAP['right']:
my_ship.rotate("left")
my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 1, ship_image, ship_info)
This:
my_ship.accel
Doesn't call the method my_ship.accel, any more than 2 calls the number 2. To call something in Python, you need parentheses. So:
my_ship.accel()
(If you're wondering why Python does it this way when other languages, like Ruby, don't… well, this means that you can use the method object my_ship.accel as a value—store it to call later, pass it to map, etc.)
But you've got another problem on top of that.
You define a method accel on Ship objects. But you also assign an integer value 10 to self.accel on Ship objects. There's no way self.accel can mean two different things at once, both the method and the number. So, which one "wins"? In this case, the self.accel = 10 happens at the time you constructed your Ship, which is later, so it wins.
So, when you write my_ship.accel, you're just referring to the number 10. And when you write my_ship.accel(), you're trying to call the number 10 as if it were a function. Hence the TypeError.
The solution is to not reuse the same name for two different things. Often, naming functions after verbs and attributes after nouns is a good way to avoid this problem—although you also have to avoid gratuitous abbreviations, because otherwise you're probably going to abbreviate acceleration and accelerate to the same accel, as you did here.

Categories