I am trying to make an animation but I am not sure how to draw a diagonal line and move it.
import pygame
import sys
WINDOW=pygame.display.set_mode((800,300))
RED=(255,0,0)
WHITE=(255,255,255)
CRIMSON=(220,20,60)
BURGUNDY=(102,0,0)
CERULEAN=(153,255,255)
PINK=(255,102,102)
FPS=100
fpsClock=pygame.time.Clock()
x=0
pygame.display.set_caption("Animation")
while True:
for event in pygame.event.get():
if event.type=="QUIT":
pygame.quit()
sys.exit()
#Animation
WINDOW.fill(CERULEAN)
x=x+1
pygame.draw.circle(WINDOW, CRIMSON, (x,100),20)
pygame.draw.circle(WINDOW, BURGUNDY, (x, 92),5)
pygame.draw.line(WINDOW, PINK, (x,30),(x,70),3)
pygame.display.update()
fpsClock.tick(FPS)
The drawing is supposed to be a fish with a triangle as its tail. I originally tried to use the polygon function but wasn't sure how to input the x and where to input the x so just decided to draw three lines for the triangle.
I just need help as to how and where I would input the x into the line or even polygon function. Like for the circle one would simply put it first but how would it be for a line and/or polygon function?
Use pygame.draw.aalines() instead.
Give it a list of points forming the triangle.
Your problem here is:
X is increasing indefinitely
It increases too fast.
If you want to make this nice, make a object called Fish, with a method move, which wil move the fish. This way you will have clean code and minimal confusion.
You must remove the old fish from the window, then draw the new one on the new position. That's why you should keep the fish in its own object.
Choose one point of the fish that will act as an universal position indicator. E.g. a nose or centre of the mass, or something. Then you just change that pos and your move method adjusts all other coordinates accordingly.
EDIT:
This is an example. I didn't try it, just put it together to show you how it is done.
I hope this will make it clearer. You see, I draw the fish once on itsown surface, then I move this surface around.
As you move the mouse, the fish will be moved.
It may be slow, and flickery, and stuff. This code has some problem as yours.
The new fish should be drawn every 4 pixels, not each one.
As fish is in an object you can have multiple fishes of different sizes.
Each fish keeps portion of a screen that replaces in an origsurf attribute.
Before it moves, it returns the screen in previous position.
So your fishes can overlap on the screen.
To make all work smooth, you will have to do some more work.
For example, there are no safeguards against going over display bounds.
As I said, it is an example on how it is done, not a full code.
class Fish:
def __init__ (self, pos=(0, 0), size=(60, 40), bgcolour=(255, 255, 255)):
self.pos = pos; self.size = size
# Use a little surface and draw your fish in it:
self.surf = surf = pygame.Surface(size)
surf.fill(bgcolour)
# Draw something:
pygame.draw.aalines(surf, (0, 0, 0), 1, [(0, 0), (0, size[1]), (size[0], size[1]/2)]) # Triangle
# Save how screen looks where fish will be:
self.origsurf = Surface(size)
sub = screen.subsurface((pos, size))
self.origsurf.blit(sub)
# Show the fish:
screen.blit(surf, pos)
pygame.display.flip()
def move (self, newpos):
# Remove the fish from old position
screen.blit(self.origsurf, self.pos)
# Save how screen looks where fish will be:
self.origsurf = Surface(self.size)
sub = screen.subsurface((newpos, self.size))
self.origsurf.blit(sub)
# Then show the fish on new location:
screen.blit(self.surf, newpos)
self.pos = newpos
pygame.display.flip()
screen = pygame.display.set_mode()
fish = Fish()
FPS=100
fpsClock=pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type=="QUIT":
pygame.quit()
sys.exit()
elif event.type==pygame.MOUSEMOTION:
fish.move(event.pos)
fpsClock.tick(FPS)
Related
I have a project where i have to create a 4 way split screen using pygame. On this screen i have to draw the same image on each of the screen just have different view of the image. I just can not figure out how to create this 4 way split screen using pygame.
I need my screen to be divided like above so i can draw my points onto each section.
I have been looking around and I can not find anything like this so any help would be great
thanks
In addition to the surface you have that gets rendered to the display, likely called something like screen, you should create another surface which all of the "action" gets drawn to. You can then use a Rect object for each quadrant of the screen which will represent the "camera" (assuming each quadrant doesn't necessarily need to show exactly the same image). When you draw back to screen, you use each camera Rect object to select a portion of the game space to draw to a specific quadrant.
# canvas will be a surface that captures the entirety of the "action"
canvas = pygame.Surface((800, 600))
# the following are your "camera" objects
# right now they are taking up discrete and even portions of the canvas,
# but the idea is that they can move and possibly cover overlapping sections
# of the canvas
p1_camera = pygame.Rect(0,0,400,300)
p2_camera = pygame.Rect(400,0,400,300)
p3_camera = pygame.Rect(0,300,400,300)
p4_camera = pygame.Rect(400,300,400,300)
On each update, you would then use these "camera" objects to blit various portions of the canvas back to the screen surface.
# draw player 1's view to the top left corner
screen.blit(canvas, (0,0), p1_camera)
# player 2's view is in the top right corner
screen.blit(canvas, (400, 0), p2_camera)
# player 3's view is in the bottom left corner
screen.blit(canvas, (0, 300), p3_camera)
# player 4's view is in the bottom right corner
screen.blit(canvas, (400, 300), p4_camera)
# then you update the display
# this can be done with either display.flip() or display.update(), the
# uses of each are beyond this question
display.flip()
There is no functions to split screen. But you can draw 4 views directly on screen or you can draw on 4 surfaces (pygame.Surface) and than blit surfaces on screen.
Since you were looking for a way to split the screen in to 4 sections and draw some points on to them I'd suggest creating 4 subsurface surfaces of the original "canvas" image for convenience.
These surfaces would act as your player(split screen) canvasses which can easily be modified.
This will enable the usage of normalized coordinates for player specific drawing purposes.
Assuming you have a screen surface set up
# Image(Surface) which will be refrenced
canvas = pygame.Surface((800, 600))
# Camera rectangles for sections of the canvas
p1_camera = pygame.Rect(0,0,400,300)
p2_camera = pygame.Rect(400,0,400,300)
p3_camera = pygame.Rect(0,300,400,300)
p4_camera = pygame.Rect(400,300,400,300)
# subsurfaces of canvas
# Note that subx needs refreshing when px_camera changes.
sub1 = canvas.subsurface(p1_camera)
sub2 = canvas.subsurface(p2_camera)
sub3 = canvas.subsurface(p3_camera)
sub4 = canvas.subsurface(p4_camera)
Now drawing on any of of the subsurfaces with these normalized coordinates
# Drawing a line on each split "screen"
pygame.draw.line(sub2, (255,255,255), (0,0), (0,300), 10)
pygame.draw.line(sub4, (255,255,255), (0,0), (0,300), 10)
pygame.draw.line(sub3, (255,255,255), (0,0), (400,0), 10)
pygame.draw.line(sub4, (255,255,255), (0,0), (400,0), 10)
# draw player 1's view to the top left corner
screen.blit(sub1, (0,0))
# player 2's view is in the top right corner
screen.blit(sub2, (400, 0))
# player 3's view is in the bottom left corner
screen.blit(sub3, (0, 300))
# player 4's view is in the bottom right corner
screen.blit(sub4, (400, 300))
# Update the screen
pygame.display.update()
Note that modifications to the subsurface pixels will affect the canvas as well. I'd recommend reading the full documentation on subsurfaces.
I am trying to make a game where if someone press left-click, pygame draws a circle, then a line connecting the circle to the upper left corner. If he/she press right-click, it removes everything created from the screen. I thought about just covering everything with a big black square, but that is not really clearing the screen, just covering it. My code is as follow:
from pygame import *
init()
size = width, height = 650, 650
screen = display.set_mode(size)
button = 0
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
def drawScene(screen, button):
# Draw circle if the left mouse button is down.
if button==1:
draw.circle(screen,GREEN,(mx,my), 10)
draw.line(screen,GREEN, (0,0),(mx,my))
if button == 3:
mouse.set_visible(False)
display.flip()
running = True
myClock = time.Clock()
# Game Loop
while running:
for e in event.get(): # checks all events that happen
if e.type == QUIT:
running = False
if e.type == MOUSEBUTTONDOWN:
mx, my = e.pos
button = e.button
drawScene(screen, button)
myClock.tick(60) # waits long enough to have 60 fps
quit()
Kindly guide me in this situation. Thanks in advance.
I thought about just covering everything with a big black square, but that is not really clearing the screen, just covering it.
There is not any difference between cover and clear. The screen consist of pixels. Drawing an object just changes the color of the pixels. Drawing a black square changes the color of the pixel again.
Note, operations like pygame.draw.circle() and pygame.draw.line() do not create any object. They just change the colors of some pixels of the surface which is passed to the operation.
The easiest way to clear the entire window is pygame.Surface.fill():
screen.fill((0, 0, 0))
respectively
screen.fill(BLACK)
In computer graphics, there is no such thing as "clearing" a screen. you can only overwrite the previous drawing with new pixels. your idea of drawing a big black rectangle would work perfectly but I may be more efficient to use screen.fill(0)
Im trying to make a rectangle bounce, without going off limits.
I want my rectangle to bounce depending on the wall it touched.
In this code im trying to bounce the rectangle in a 90º angle, but it isn't working.
Im using this to calculate each advance:
rect_x += rectxSpeed
rect_y += rectySpeed
When it reachs the limit
if rect_y>450 or rect_y<0:
rectySpeed=5
rect_y=rectySpeed*-(math.pi/2)
if rect_x>650 or rect_x<0:
rectxSpeed=5
rectx_y=rectxSpeed*-(math.pi/2)
Whole code here:
import pygame
import random
import math
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
rect_x= 50.0
rect_y = 50.0
rectxSpeed=5
rectySpeed=5
pygame.init()
# Set the width and height of the screen [width, height]
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
# --- Main event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# --- Game logic should go here
# --- Screen-clearing code goes here
# Here, we clear the screen to white. Don't put other drawing commands
# above this, or they will be erased with this command.
# If you want a background image, replace this clear with blit'ing the
# background image.
screen.fill(BLACK)
string=str(rect_x)
string2=str(rect_y)
string3="["+string+"]"+"["+string2+"]"
font = pygame.font.SysFont('Calibri', 25, True, False)
text = font.render(string3,True,RED)
screen.blit(text, [0, 0])
#Main rectangle
pygame.draw.rect(screen, WHITE, [rect_x, rect_y, 50, 50])
#Second rectangle inside the rectangle 1
pygame.draw.rect(screen, RED, [rect_x+10, rect_y+10, 30, 30])
rect_x += rectxSpeed
rect_y+=rectySpeed
if rect_y>450 or rect_y<0:
rectySpeed=5
rect_y=rectySpeed*-(math.pi/2)
if rect_x>650 or rect_x<0:
rectxSpeed=5
rect_x=rectxSpeed*-(math.pi/2)
# --- Drawing code should go here
# --- Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# --- Limit to 60 frames per second
clock.tick(20)
# Close the window and quit.
pygame.quit()
¿How can i adjust the advance?
This code produce this:
By Changing the reach limit code with:
if rect_y>450 or rect_y<0:
rectySpeed=rectySpeed*-(math.pi/2)
if rect_x>650 or rect_x<0:
rectxSpeed=rectxSpeed*-(math.pi/2)
Produces this:
I think it's important to recognize here that the speed of the rectangle object here is a scalar value, not it's vector counterpart velocity. You are attempting to multiply the rectySpeed (a scalar value) by -(math/pi)/2, which is a value that will be returned in radians as #0x5453 mentioned. As far as the rectangle bouncing differently depending on the wall that it contacted, you have not specified the differing constraints that you intend to impose. I think you may want to consider why you want the rectangle to always bounce at a 90° angle. Note that a rectangle that always bounces at a 90° angle would yield some very odd functionality to the player/user.
The angle that the rectangle is approaching the wall measured from the horizontal will be equal to the angle that the rectangle will rebound off the wall measured from the vertical line of the wall to it's new path (in the case of an x-directional bounce).
In terms of an accurate physics engine, you may want to consider just simplifying your mechanics to the following in the case that the rectangle contacts a wall:
if rect_y>450 or rect_y<0:
rectySpeed=rectySpeed*-1
if rect_x>650 or rect_x<0:
rectxSpeed=rectxSpeed*-1
The above ensures that the rectangle object simply changes direction, therefore the magnitude of the rectangle's speed does not change, however the velocity indeed does since it is a vector quantity.
I have a game where the bullet is moving so fast that in a single frame it is already across the screen. That being said, it has already collided with multiple walls. Currently, I have a rectangular image that spans from where the bullet currently is, to where the bullet will be in the next frame, in order to not miss any zombies that may be in between.
I also kill the bullet if it collided with any wall, before checking if it collided with any of the zombies because what happened was that if there was a zombie behind the wall and I checked for the zombie collisions first, it would kill the zombie and then kill the bullet.
So basically, I would like to know a way to find the coordinates of where the bullet collided with the wall so that instead of advancing the bullet at its full speed, I will just advance it to just before where the collision is, check for zombie collisions, and then kill the bullet.
I am using mask collision.
If the bullets travel too fast to collide with the walls or enemies, you need ray casting (or alternatively move the bullets in multiple small steps). Here's a simple ray casting example that returns the closest collision point. I use vectors and pygame.Rect.collidepoint to see if a point along the heading vector collides with an obstacle.
import sys
import pygame as pg
from pygame.math import Vector2
class Wall(pg.sprite.Sprite):
def __init__(self, x, y, w, h, *groups):
super().__init__(*groups)
self.image = pg.Surface((w, h))
self.image.fill(pg.Color('goldenrod4'))
self.rect = self.image.get_rect(topleft=(x, y))
def ray_cast(origin, target, obstacles):
"""Calculate the closest collision point.
Adds the normalized `direction` vector to the `current_pos` to
move along the heading vector and uses `pygame.Rect.collidepoint`
to see if `current_pos` collides with an obstacle.
Args:
origin (pygame.math.Vector2, tuple, list): Origin of the ray.
target (pygame.math.Vector2, tuple, list): Endpoint of the ray.
obstacles (pygame.sprite.Group): A group of obstacles.
Returns:
pygame.math.Vector2: Closest collision point or target.
"""
current_pos = Vector2(origin)
heading = target - origin
# A normalized vector that points to the target.
direction = heading.normalize()
for _ in range(int(heading.length())):
current_pos += direction
for sprite in obstacles:
# If the current_pos collides with an
# obstacle, return it.
if sprite.rect.collidepoint(current_pos):
return current_pos
# Otherwise return the target.
return Vector2(target)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
walls = pg.sprite.Group()
Wall(100, 170, 90, 20, all_sprites, walls)
Wall(200, 100, 20, 140, all_sprites, walls)
Wall(400, 60, 150, 100, all_sprites, walls)
pos = Vector2(320, 440)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.update()
collision_point = ray_cast(pos, pg.mouse.get_pos(), walls)
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.draw.line(screen, (50, 190, 100), pos, pg.mouse.get_pos(), 2)
pg.draw.circle(screen, (40, 180, 250), [int(x) for x in collision_point], 5)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
Unfortunately PyGame doesn't give us a built-in means of returning collision points easy way of doing this so there is a bit of leg work to do.
However, before I explain that, you mentioned in your question that the bullet is moving very fast. I'm not sure how fast we are talking so this might not apply, but in my experience collision becomes a hit and miss at high speeds, especially if you're working on a slower computer.
Assuming that ^ isn't applicable:
We can use pygame.Rect.colliderect to trigger an if statement.
if <bullet-rect>.collidepoint(<wall-rect>):
print(bullet_x, bullet_y)
Simply swap out and for the actual rects and you should be good to go. One thing to note is that if the bullet is moving right to left, you will have to add the bullet's width to the x value, and if the bullet is moving top to bottom, you will have to add the bullet's height to the y value.
Note: Remember to add pygame.Rect(<bullet-rect>) and pygame.Rect(<wall-rect>) to each value or you'll get an error.
I am a beginner in Pygame. I have coded a function for moving two balls in different direction and I follow the instructions coding it but it seems to be not working. I can draw two balls in screen but they will not move. I fixed it for almost 1 hour but no idea why balls aren't moving.
So, Can someone helps me check my code and just give me some hints. I will really appreciate anyone who helps me!
My code shows below
import pygame,sys,time
from pygame.locals import *
# User define function
def ball_move(Surface,white,pos,rad,speed):
size=Surface.get_size()
for item in [0,1]:
pos[item]=pos[item]+speed[item]
if pos[item]<rad:
speed[item]=-speed[item]
if pos[item]+rad>size[item]:
speed[item]=-speed[item]
# Open a brand-new window
pygame.init()
Screen_size = (500,400)
Title = ('Pong')
Frame_Delay = 0.01
Surface= pygame.display.set_mode(Screen_size,0,0)
pygame.display.set_caption(Title)
# Set up white color for drawing balls
white=pygame.Color('white')
# Now, we start to draw two balls
pos1=(100,200)
pos2=(400,200)
rad=10
ball1=pygame.draw.circle(Surface,white,pos1,rad,0)
ball2=pygame.draw.circle(Surface,white,pos2,rad,0)
pygame.display.update()
# Now, define speed
speed1=(2,-2)
speed2=(-2,2)
# Now, we define a loop
while ball1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Now, we move the ball
ball1move=ball_move(Surface,white,pos1,rad,speed1)
ball2move=ball_move(Surface,white,pos2,rad,speed2)
pygame.draw.circle(Surface,white,pos1,rad,0,0)
pygame.draw.circle(Surface,white,pos2,rad,0,0)
surface.fill(pygame.Color('black'))
Part of saulspatz answer is correct, part is incorrect. You don't have to use sprites if you dont want to. pygame.draw is not pretty but perfectly usable. The main problem does seem to be your understanding of what to do in your event loop. All this should go in it:
while running:
# Handdle your events
# update your state
# draw to your display
pygame.display.update()
Also I notice in your unreachable code after the loop you are filling after your draws. Remember whether you fill, blit, or draw the latest thing goes over the rest. So for your example:
import pygame ,sys, time
from pygame.locals import *
# User define function
def ball_move(surface, pos, rad, speed):
def _add(l_pos, l_speed, l_size):
l_pos += l_speed
if l_pos <= rad or l_pos >= l_size - rad:
l_speed = -l_speed
return l_pos, l_speed
size = surface.get_size()
pos_x, speed_x = _add(pos[0], speed[0], size[0])
pos_y, speed_y = _add(pos[1], speed[1], size[1])
return (pos_x, pos_y), (speed_x, speed_y)
pygame.init()
screen_size = (500, 400)
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption('Pong')
running = True
pos1 = (100, 200)
pos2 = (400, 200)
speed1 = (2, -2)
speed2 = (-2, 2)
rad = 10
while running:
# Handdle your events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# update your state
pos1, speed1 = ball_move(screen, pos1, rad, speed1)
pos2, speed2 = ball_move(screen, pos2, rad, speed2)
# draw to your display
screen.fill(pygame.Color('black'))
pygame.draw.circle(screen, pygame.Color('white'), pos1, rad)
pygame.draw.circle(screen, pygame.Color('white'), pos2, rad)
pygame.display.update()
There are a lot of problems with your code. The most basic is that you haven't figured out how event-driven programming works. You need to put the code that moves the balls inside your loop.
Another problem is that I don't think you want to use the pygame.draw module. It's been a long time since I wrote any pygame scripts, but as I remember, this module is useful for drawing fixed objects, like the background. A quick look at the docs seems to confirm this.
For moving objects, I think you need to look at the pygame.sprite module. Even if you got this code to work, it wouldn't move the balls. It would just draw a new ball at another position. So you would have first two balls, then four, then eight, ... . Sprites actually move. Not only is the object drawn at the new position, but it's erased at the old position. Your code don't address erasure at all.
Hope this helps.