Pygame: Sprite changing due to direction of movement - python

I've just started learning how to use pygame yesterday. I was read this one book that was super helpful and followed all its tutorials and examples and stuff. I wanted to try making a really simple side scroller/platforming game but the book sorta jumped pretty fast into 3D modeling with out instructing how to make changing sprites for movement of up down left and right and how to cycle through animating images.
I've spent all today trying to get a sprite to display and be able to move around with up down left and right. But because of the simple script it uses a static image and refuses to change.
Can anyone give me some knowledge on how to change the sprites. Or send me to a tutorial that does?
Every reference and person experimenting with it ha always been using generated shapes so I'm never able to work with them.
Any help is very appreciated.
Added: before figuring out how to place complex animations in my scene I'd like to know how I can make my 'player' change to unmoving images in regards to my pressing up down left or right. maybe diagonal if people know its something really complicated.
Add: This is what I've put together so far. http://animania1.ca/ShowFriends/dev/dirmove.rar would there be a possibility of making the direction/action set the column of the action and have the little column setting code also make it cycle down in a loop to do the animation? (or would that be a gross miss use of efficiency?)

Here is a dumb example which alernates between two first images of the spritesheet when you press left/right:
import pygame
quit = False
pygame.init()
display = pygame.display.set_mode((640,480))
sprite_sheet = pygame.image.load('sprite.bmp').convert()
# by default, display the first sprite
image_number = 0
while quit == False:
event = pygame.event.poll()
no_more_events = True if event == pygame.NOEVENT else False
# handle events (update game state)
while no_more_events == False:
if event.type == pygame.QUIT:
quit = True
break
elif event.type == pygame.NOEVENT:
no_more_events = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
image_number = 0
elif event.key == pygame.K_RIGHT:
image_number = 1
event = pygame.event.poll()
if quit == False:
# redraw the screen
display.fill(pygame.Color('white'))
area = pygame.Rect(image_number * 100, 0, 100, 150)
display.blit(sprite_sheet, (0,0), area)
pygame.display.flip()
I've never really used Pygame before so maybe this code shoudln't really be taken as an example. I hope it shows the basics though.
To be more complete I should wait some time before updating, e.g. control that I update only 60 times per second.
It would also be handy to write a sprite class which would simplify your work. You would pass the size of a sprite frame in the constructor, and you'd have methodes like update() and draw() which would automatically do the work of selecting the next frame, blitting the sprite and so on.
Pygame seems to provide a base class for that purpose: link text.

dude the only thing you have to do is offcourse
import pygame and all the other stuff needed
type code and stuff..........then
when it comes to you making a spri
class .your class nam here. (pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.init(self)
self.image=pygame.image.load(your image and path)
self.rect=self.image.get_rect()
x=0
y=0
# any thing else is what you want like posistion and other variables
def update(self):
self.rect.move_ip((x,y))
and thats it!!!! but thats not the end. if you do this you will ony have made the sprite
to move it you need

I don't know much about Pygame, but I've used SDL (on which Pygame is based).
If you use Surface.blit(): link text
You can use the optional area argument to select which part of the surface to draw.
So if you put all the images that are part of the animation inside a single file, you can select which image will be drawn.
It's called "clipping".
I guess you will have a game loop that will update the game state (changing the current image of the sprite if necessary), then draw the sprites using their state.

Related

How can i stop my square from moving in pygame?

I am using python to create a snake game , as for now im trying to create a square that moves in a direction set by pressing an ARROW KEY, as example:
if Right Arrow as been pressed, the square suppose to move in that direction until some other Arrow Key is pressed, i tried many diffrent things but nothing has worked for me so far. it actually moves where i want in the speed i want, but i cant change its direction
help will be apriciated.
my code:
I like your approach to solving this problem, but I feel that there is a better way. You can set a variable equal to the direction the snake is moving, like direction = "right". Then, every frame, you can move the current set direction and check for new user input, like this:
# Move your snake - You know how to do this, right?
for event in pygame.event.get():
if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
direction = "right"
# Continue for all four directions
Hope this helps!

Pygame Display Update Glitch?

I have some code that is kinda funky. Basically, the variable gameStart is in a variable that uses the function, cursorOver, which finds and detects where and if the mouse button is and if it is pressed. I have 3 buttons and I want each button to become larger when the cursor is over the button. The first button implementation works. However, if I try to add another button, the button becomes enlarged, however, the button starts flickering.
window.blit(background,(0,0))
window.blit(title,(175,200))
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.draw.rect(window,PURPLE,(550,400,200,100),0)
close()
mouseX,mouseY = pygame.mouse.get_pos()
mouseClick = pygame.mouse.get_pressed()[0]
gameStartC = cursorOver(50,400,200,100,mouseX,mouseY)
instructionStartC = cursorOver(300,400,200,100,mouseX,mouseY)
objectiveStartC = cursorOver(550,400,200,100,mouseX,mouseY)
nextStartC = cursorOver(580,250,175,100,mouseX,mouseY)
if gameStartC == True:
while True:
pygame.draw.rect(window,PURPLE,(25,375,250,150),0)
pygame.display.update()
break
else:
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
pygame.display.update()
#this is the part where the code becomes glitchy
if instructionStartC == True:
while True:
pygame.draw.rect(window,PURPLE,(275,375,250,150),0)
pygame.display.update()
break
else:
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.display.update()
It's simply because you call pygame.display.update() multiple times.
You should create a standard game loop that typically does these three things:
handle input
update state
draw to screen
and then repeats.
In the 'draw to screen'-step, you draw all your sprites/rects/whatever to the screen surface, and then eventually call pygame.display.update() once at the end.
Calling pygame.display.update() multiple times, not clearing the screen between iterations of the loop and creating multiple unnecessary event loops are common beginner mistakes that lead to those kind of glitches IMHO.
So in your case, the code should probably look more like this:
if gameStartC:
pygame.draw.rect(window,PURPLE,(25,375,250,150),0)
else:
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
if instructionStartC:
pygame.draw.rect(window,PURPLE,(275,375,250,150),0)
else:
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.display.update()
I don't know what you expected the while-loops to do, and maybe you should use pygames Rect and Sprite classes. It will make your life easier.

I don't know what I am doing wrong. Code for opening a pygame window

import pygame
import sys
from pygame.locals import *
DISPLAY_SURF = pygame.display.set_mode((640,480))
#Sets the resolution to 640 pixels by 720 pixels
class Game:
def __init__(self):
pygame.init()
self.FPS = 60
self.fps_clock = pygame.time.Clock()
self.surface = pygame.display.set_mode((640, 480))
pygame.display.set_caption("The Hunt")
img = pygame.image.load("Graphics/background.png")
self.surface.blit(img)
#This class sets the basic attributes for the window.
#The clock is set to 60 and the name of the window
#is set to The Hunt which is a working title for my project
def run(self):
while True:
pygame.display.update()
self.fps_clock.tick(self.FPS)
self.process_game()
#This updates the window display to refresh every clock tick
def process_game(self):
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
game = Game()
#This creates an instance of the class Game which gives all the attributes
# and behaviours to this instance
game.run()
#Calling this function generates a window with the attributes defined.
I need some help. I have already checked if it is in the same folder, the file is definitely a png and I spelt all the folder names and the destination correctly. I'm open to any suggestions
I'm going to answer this question despite that it is not really a good one for Stack Overflow. On this site, you'll have to be more specific and detailed, because no one intends to read through a huge lot of code for you. I did however pickup some things that I think can be fixed ( some of these are opinion based, something that your question should never force an answer to have), but... here it is anyway:
For starters, when you construct a class, you use parenthesis after the class name, even if it's not going to inherit anything form another class. So change the line where you construct the Game class to this:
class Game():
Second thing about this code is that if your going to create the pygame window surface inside the Game() class, I don't understand why you're creating another window at the beginning of your code. If there is a reason for this please explain it in a comment in your code.
The last thing is more opinion-based . I don't know how many people create Pygame GUI applications like this, but it would be simpler to not use classes so you could understand the code better. When I create a Pygame GUI, I define the window, then the sprites, then I run the main game loop in a While Loop. Here is how I would normally structure your program:
#import pygame
import pygame, sys
from pygame.locals import *
#Initialize pygame and define colours
pygame.init()
white = 255,255,255
#Sets the resolution to 640 pixels by 720 pixels and caption for pygame window
DISPLAY_SURF = pygame.display.set_mode((640,480))
pygame.display.set_caption("The Hunt!")
#Create a clock object
clock = pygame.time.Clock()
FPS = 60
#Define a variable to refer to image
image = pygame.image.load("download.jpg")
#Start main loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAY_SURF.fill(white)
DISPLAY_SURF.blit(image,(0,0))
pygame.display.update()
I do use classes when creating a sprite, so I can create several instances of it, as well as keep functions I want to perform on the sprite all in one place. Doing this for the WHOLE program does work, and I suppose it's more "pythonic" (since python is object-oriented) but is still unnecessary for something like this. Here is a reference that teaches pygame in a similar way to how I code it, which I personally find to be an excellent tutorial.
Many people also put this code in a main() function and then run it, which is also a wildly used and accepted practice.

How to move from one screen to another, with any input from the user, in pygame? [duplicate]

I'm making a little game and I want to make another window separately from my main one.
I have the the main game in a main window, and I want to open a new window and do a little animation when the user does something.
In my example code below, when the user presses "a" I want it to open a new window and blit to there.
Here I set up the two windows: (I know this doesnt work, its what I'm asking how to do)
SCREEN_X = 400
SCREEN_Y = 400
BSCREEN_X = 240
BSCREEN_Y = 160
BATTLE_SCENE = pygame.display.set_mode((BSCREEN_X, BSCREEN_Y))
SCREEN = pygame.display.set_mode((SCREEN_X, SCREEN_Y))
and then the program:
def run_ani ():
#Do animation, blitting to BATTLE_SCENE
return
def main_game():
ending=False
while ending==False:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT: ending=True
if event.type == KEYDOWN: # key down or up?
if event.key == K_ESCAPE:
ending=True # Time to leave
print("Stopped Early by user")
elif event.key == K_a:
run_ani()
#Normal screen motion, blitting to SCREEN
if ending: pygame.quit()
return
So far what this does is draws the main screen, then when A is pressed, it stops drawing the main screen animations, but still draws the other animations on the main screen and draws in the top left corner.
I'm pretty sure it does this because I am setting BATTLE_SCENE to be smaller than the main screen, thus when blitting to BATTLE_SCENE it blits to the area I created (240x160) in the top corner of the main screen.
However I want BATTLE_SCENE to be a seperate window, so that when I press 'a' it will pop up, do its thing, then close or at least go behind the main screen.
How to do this? Is it even possible?
Do you really need multiple windows? I mean, do you really need them?
If yes, then you should probably use pyglet/cocos2d instead.
To have multiple windows in pygame, you need multiple processes (one for each window). While this is doable, it's not worth the efford. You'll need IPC to exchange data between the windows, and I guess your code will become error-prone and ugly.
Go with pyglet when you need more than one window.
The better solution is probably to divide your game into scenes. Create multiple scenes so that each one represent one stage of the game, something like MenuScene, MainScene, BattleScene, GameOverScene, OptionScene etc.
Then let each of those scenes handle input/drawing of that very part of the game.
MenuScene handles drawing and input etc. of the game's menu
MainScene handles drawing and input etc. of the running game
BattleScene handles drawing and input etc. of whatever you do in run_ani
In your mainloop, just pass control over to the current scene by implementing the methods draw(), handle_event(), and update().
Some example code to get the idea:
scenes = {'Main': MainScene(),
'Battle': BattleScene()} #etc
scene = scenes['Main']
class MainScene():
...
def handle_event(self, event):
if event.type == KEYUP:
if event.key == K_a:
scene = scenes['Battle']
...
class BattleScene():
...
def draw(self):
# draw your animation
def update(self):
# if animation is over:
scene = scenes['Main']
...
def main_game():
ending=False
While Not ending:
clock.tick(30)
for event in pygame.event.get():
scene.handle_event(event)
scene.update()
scene.draw()
This is an easy way to cleanly seperate the game logic and allow context switching.
======================================Edit=========================================
Actually it won't work. Apperantly pygame only supports one display screen, and when you initialize another, it will close the first. You will stay with two varibles, which in fact are the same surface. You can have instead the game increasing the window size and playing the battle scene on the side of it, to do this, you can call the pygame.display.set_mode() again with different values. The varible which references the display screen will still be usable, as it change its reference to the new one. After the scene is over you can decrease the window back the same way.
==================================================================================
What basically happens is you run a loop, and each iteration of it is rendering and displaying a new frame.
When you call a function inside a loop, it doesn't continue to run until you finish running the function.
One way to solve this problen is just keep calling the function that updates the battle scene in the main loop.
Another way is by using threading. Threading is basically running multiple scripts ("Threads") in the same time.
Luckily, python already implemented this for us with the threading module.
It's too long for me to explain the module here, but you can learn it here. It might be a little complex if you haven't use threads before, but after some time it will be easier.
And If you want to learn more about threading you can go here.
Specificly here, you can have two threads, one for each loop/window, and run them in the same time.
Hope I helped you!
Yes, that is possible. SDL2 is able to open multiple windows. In the example folder you can take a look at "video.py".
https://github.com/pygame/pygame/blob/main/examples/video.py
"This example requires pygame 2 and SDL2. _sdl2 is experimental and will change."

How to get a .Rect to follow another based on the x,y x direction and y direction on the one in front?

I've been working on a snake code for quite a while now. It is still rather primitive and very very basic. I have gotten the collisions to work, food generation and very basic movement. What I'm struggling on is figuring out the movement. Whenever you press WASD or the Arrow Keys it would move the head, and the "body" of the snake follows the part in front of it. So snakecol[1] follows snakecol[0] and so on.
Thank you in advance for any tips and pointers on how to improve my code.
#W to move up, A to move to the left, S to move down, D to move right.
#Snake by Juan Jaramillo
import pygame
import sys
import random
import math
import time
pygame.init()
screen=pygame.display.set_mode((500,500))
point=3
def collision():
global foodx
global foody
global x
global y
global xd
global yd
global point
global snakecol
if snakecol[0].colliderect(foodcol):
foodx=random.randint(0,24)*20
foody=random.randint(0,24)*20
point+=1
snakecol.append(snakecol[len(snakecol)-1])
if xd==20:
snakecol[len(snakecol)-1].x-=20
if xd==-20:
snakecol[len(snakecol)-1].x+=20
if yd==20:
snakecol[len(snakecol)-1].y-=20
if yd==-20:
snakecol[len(snakecol)-1].y==20
print "Snake is",point,"long."
red=(255,0,0)
blue=(0,0,255)
green=(0,255,0)
black=(0,0,0)
block1=0
block2=1
count=0
screen.fill(black)
randFood=random.randint(0,24)
x=60
y=0
foodx=randFood*20
foody=randFood*20
snakecol=list()
snakecol.append(pygame.Rect(x,y,20,20))
snakecol.append(pygame.Rect(x-20,y,20,20))
snakecol.append(pygame.Rect(x-40,y,20,20))
foodcol=pygame.Rect(foodx,foody,20,20)
xd=[20]
yd=[0]
##snakecol=pygame.Rect(x,y,20,20)
foodcol=pygame.Rect(foodx,foody,20,20)
done=False
while not done:
screen.fill(black)
collision()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#controls
if block1==1:
if(pygame.key.get_pressed()[pygame.K_a]) or (pygame.key.get_pressed()[pygame.K_LEFT]):
yd[0]=0
xd[0]=-20
block1=0
block2=1
if block1==1:
if(pygame.key.get_pressed()[pygame.K_d]) or (pygame.key.get_pressed()[pygame.K_RIGHT]):
yd[0]=0
xd[0]=20
block1=0
block2=1
if block2==1:
if(pygame.key.get_pressed()[pygame.K_w]) or (pygame.key.get_pressed()[pygame.K_UP]):
xd[0]=0
yd[0]=-20
block1=1
block2=0
if block2==1:
if(pygame.key.get_pressed()[pygame.K_s]) or (pygame.key.get_pressed()[pygame.K_DOWN]):
xd[0]=0
yd[0]=20
block1=1
block2=0
#stop moving paddle
for m in range(0,len(snakecol)):
if snakecol[m].x<-20:
snakecol[m].x=500
if snakecol[m].x>500:
snakecol[m].x=-20
if snakecol[m].y<-20:
snakecol[m].y=500
if snakecol[m].y>500:
snakecol[m].y=-20
for m in range(0,len(snakecol)):
snakecol.pop
pygame.draw.rect(screen,(255,255,255),snakecol[m],0)
pygame.draw.rect(screen,(0,0,0),snakecol[m],1)
snakecol[m].x+=xd[0]
snakecol[m].y+=yd[0]
foodcol.x=foodx
foodcol.y=foody
pygame.draw.rect(screen,(255,255,255),(foodx,foody,20,20),0)
pygame.draw.rect(screen,(0,0,0),(foodx,foody,20,20),1)
pygame.display.flip()
time.sleep(0.4)
count+=1
pygame.quit()
The body blocks don't need to know the direction. You can just append a new head block (with the next x, y coords) to the list and remove the last block.
You could also use a collections.deque, pronounced "deck", a double ended queue which allows fast appends and pops on either end (although efficiency isn't really needed here, the code just looks a bit nicer).
A few more suggestions, you should check out how functions, classes and Pygame sprites work. There's a nice, free book about Python and Pygame called Program Arcade Games. Also, you shouldn't use global variables for everything, since they can make code really hard to understand (use classes).
Edit: To emphasize my answer above: Don't move the rects (the parts of the snake) in your list directly by changing their x and y values. Just pop or remove the last rect and append a new head rect. This will create the illusion of movement but the rects actually all stay at the same position.
I mentioned the deque, because it has efficient methods for popping and appending at the left end. For lists this is very inefficient, but I think it won't matter in your case, because the list won't grow too much and computers nowadays are very powerful.
Some code review (this should rather be in https://codereview.stackexchange.com/ though):
Line 132: You don't call pop because you forgot the parentheses. But if you actually called it, the snakecol list would shrink and the game would crash.
Handle the event loop first, then the game logic (e.g. collisions) and then the drawing. In a game class you would use different methods for these parts of the program.
You call pygame.key.get_pressed() several times. Instead assign the keys list to a variable keys = pygame.key.get_pressed() and use this variable later in the loop.
time.sleep is usually not used in Pygame (the game controls will seem to be unresponsive). There's the pygame.time.Clock class which you can use to limit the framerate and you can use a timer variable to keep track of when the snake can move. Instantiate a clock before the main loop clock = pygame.time.Clock() and in the loop you call clock.tick(enter_max_fps_here) every frame.

Categories