Scrolling in 2D game? - python
I'm trying to add a scrolling "camera" that follows the player when it moves but can't figure out how to do this. I know that you can just move the level in the opposite direction when you press one of the movement keys but I'd rather not do that as I plan on adding enemies later on and don't want have to keep update their coordinates as the player moves.
I've added my code with a sample level below.
Code:
import pygame, sys, time, random, math
from pygame.locals import *
BACKGROUNDCOLOR = (255, 255, 255)
WINDOWW = 800
WINDOWH = 600
PLAYERW = 66
PLAYERH = 22
FPS = 60
MOVESPEED = 3
YACCEL = 0.13
GRAVITY = 2
BLOCKSIZE = 30
pygame.init()
screen = pygame.display.set_mode((WINDOWW, WINDOWH), 0, 32)
mainClock = pygame.time.Clock()
testLevel = [
(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,),
(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,)]
def createblock(length, height, color):
tmpblock = pygame.Surface((length, height))
tmpblock.fill(color)
tmpblock.convert()
return tmpblock
def terminate(): # Used to shut down the software
pygame.quit()
sys.exit()
def add_level(lvl, bSize): # Creates the level based on a map (lvl) and the size of blocks
bList = [] # List of every block
bListDisp = [] # List of every block to display
bTypeList = [] # List with corresponding type of block(wall, air, etc.)
for y in range(len(lvl)):
for x in range(len(lvl[0])):
if lvl[y][x] == 0: # If the block type on lvl[y][x] is '0', write "air" down in the type list
bTypeList.append("air")
elif lvl[y][x] == 1: # If the block type on lvl[y][x] is '1', write "wall" down in the type list
bTypeList.append("solid")
bList.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block that is registered
bListDisp.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block to display that is registered
return bList, bListDisp, bTypeList
player = pygame.Rect((WINDOWW/2), (WINDOWH - BLOCKSIZE*3), PLAYERW, PLAYERH)
wallblock = createblock(BLOCKSIZE, BLOCKSIZE,(20,0,50))
lastTime = pygame.time.get_ticks()
isGrounded = False
vx = 0
vy = 0
allLevels = [testLevel] # A list containing all lvls(only one for now)
maxLevel = len(allLevels) # Checks which level is the last
currLevel = allLevels[0] # Current level(start with the first lvl)
blockList, blockListDisp, blockTypeList = add_level(currLevel, BLOCKSIZE) # A list with every block and another list with the blocks types
thrusters = True
jumping = False
falling = True
while True:
"""COLLISION"""
collision = False
for i in range(len(blockTypeList)):
if blockTypeList[i] == "solid":
if player.colliderect(blockList[i]):
collision = True
if vx > 0 and not falling:
player.right = blockListDisp[i].left
vx = 0
print('Collide Right')
if vx < 0 and not falling:
player.left = blockListDisp[i].right
vx = 0
print('Collide Left')
if vy > 0:
player.bottom = blockListDisp[i].top
isGrounded = True
falling = False
vy = 0
print('Collide Bottom')
if vy < 0:
player.top = blockListDisp[i].bottom
vy = 0
print('Collide Top')
else:
player.bottom += 1
if player.colliderect(blockList[i]):
collision = True
#isGrounded = True
#falling = False
player.bottom -= 1
if not collision:
falling = True
isGrounded = False
# Input
pressedKeys = pygame.key.get_pressed() # Checks which keys are being pressed
timeDiff = pygame.time.get_ticks() - lastTime # Calculates time difference
lastTime += timeDiff # Last time checked reset to current time
# Shut-down if the ESC-key is pressed or the window is "crossed down"
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYDOWN and event.key == K_ESCAPE:
terminate()
"""X-axis control"""
if pressedKeys[ord('a')]:
vx = -MOVESPEED
if pressedKeys[ord('d')]:
vx = MOVESPEED
if not pressedKeys[ord('d')] and not pressedKeys[ord('a')]:
vx = 0
"""Y-axis control"""
# Controls for jumping
if pressedKeys[ord('w')] and thrusters == True:
vy -= YACCEL * timeDiff; # Accelerate along the y-xis when "jumping", but not above/below max speed
if vy <= -4:
vy = -4
isGrounded = False # You are airborne
jumping = True # You are jumping
if event.type == KEYUP: # If you let go of the "jump"-button, stop jumping
if event.key == ord('w') and vy < 0 and not isGrounded:
jumping = False
falling = True
player.x += vx
player.y += vy
# Gravity
if not isGrounded or falling:
vy += 0.3
if vy > 80:
vy = 80
screen.fill(BACKGROUNDCOLOR)
for i in range(len(blockTypeList)):
if blockTypeList[i] == "solid":
screen.blit(wallblock, (blockListDisp[i].x, blockListDisp[i].y)) #blit the wall-block graphics
pygame.draw.rect(screen, (0, 0, 0), player)
pygame.display.update()
mainClock.tick(FPS)
The trick is to keep track of camera coordinates and use these as an offset in your rendering code. It looks like you're doing you're rendering right at the end of the code you've posted, drawing each block with coord x,y to pixel x,y on the screen.
As you say, shifting the level around isn't great. Instead, have your key inputs (or other camera moving device) change cameraX and cameraY variables, and then add (or subtract, depending which direction you want to go) these values from the block x and y values to change which pixels map to which blocks. I.e. change your rendering to:
screen.blit(wallblock, (blockListDisp[i].x + cameraX, blockListDisp[i].y + cameraY))
This means if your camera moves to (10, 20) then you map your block at (5, 5) to (15, 25) on the screen, shifting your whole level across while your underlying model of the level stays the same. Make sense?
You can also take this slightly further; if your camera is only being moved to follow your character you can make swap cameraX and cameraY in the above for some function of the character position, and have the whole thing just managed directly there.
Related
Python Pygame changing two positions based on a angle [duplicate]
This question already has answers here: How to set the pivot point (center of rotation) for pygame.transform.rotate()? (5 answers) How can you rotate an image around an off center pivot in Pygame (1 answer) Closed 4 months ago. Basically im trying to make a line point out out of the muzzle of this gun. Picture of player, gun and make shift cross hair for reference (not part of the game) How i made the gun was by get_rect() the gun surface, and setting the rectangles pos values to the players right side. I also made it so the gun will rotate to always point towards the mouse, but when the cross hair goes past the players right side (the angle goes past 90 or -90) the gun will flip to the players left side. Sorry for very messy code, im still quite new. gunxposmdl = Player.player_rect.right gunyposmdl = Player.player_rect.top gunyposmdl += 20 mouse_x, mouse_y = pygame.mouse.get_pos() rel_x, rel_y = mouse_x - gunxposmdl, mouse_y - gunyposmdl angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) if angle > 90 or angle < -90: if angle > 0: angle =-abs(angle) else: angle = abs(angle) rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle)) rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl)) player_surf_cpy = Player.player_surf.copy() player_fliped = pygame.transform.flip(player_surf_cpy, False, True) flipgunxposmdl = Player.player_rect.left rshotgunmdlcpy = rshotgunmdl.copy() rshotgunmdlfliped = pygame.transform.flip(rshotgunmdlcpy, False, True ) rshotgunrectfliped = rshotgunmdlfliped.get_rect(midright=(flipgunxposmdl, gunyposmdl)) screen.blit(player_fliped, Player.player_rect) screen.blit(rshotgunmdlfliped, rshotgunrectfliped) sg_muzzle_x, sg_muzzle_y = rshotgunrectfliped.midleft[0], rshotgunrectfliped.midleft[1] else: rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle)) rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl)) screen.blit(Player.player_surf, Player.player_rect) screen.blit(rshotgunmdl, rshotgunrect) sg_muzzle_x, sg_muzzle_y = rshotgunrect.midright[0], rshotgunrect.midright[1] #Cast rays pygame.draw.line(screen, ("Green"), (sg_muzzle_x, sg_muzzle_y), (sg_muzzle_x, sg_muzzle_y), 3) The problem with this is that when the guns surface is rotated, the rectangle doesnt rotate with it. So when i rotate the gun with the mouse, the line still gets drawn where the rectangles right side is and not where the muzzle is. I need the line to stick out of the muzzle as a visual on where the gun is pointing. Im going to be using this to implement hit casting. E.g. Image example (actual in game cross hair this time) Could somebody please help me make it so the line is coming out of the muzzle in both examples please? full code: #!/bin/env python3 import pygame from sys import exit import time import math pygame.init() video_infos = pygame.display.Info() width, height = video_infos.current_w, video_infos.current_h screen = pygame.display.set_mode((width, height), pygame.FULLSCREEN, pygame.RESIZABLE) pygame.display.set_caption('2D Quake') clock = pygame.time.Clock() keys = pygame.key.get_pressed() mousec = pygame.image.load("textures/UI/crossh.png").convert_alpha() pygame.mouse.set_visible(False) backround = pygame.Surface((width, height)).convert_alpha() backround.fill('White') sbar = pygame.image.load("textures/UI/HUD/SBAR.png").convert_alpha() class Player: player_surf = pygame.Surface((50,100)) player_surf.fill('Black') player_rect = player_surf.get_rect(midbottom = (300, height)) player_gravity = 0 player = {"Inv": [1,1,0], "Hand": 1, "Health": 100} class Weapons: shotgun = pygame.image.load("textures/weapons/SHOTGUN.png").convert_alpha() class Enemies: esoldier_surf = pygame.Surface((50,100)) esoldier_surf.fill('Black') esoldier_rect = esoldier_surf.get_rect(midbottom = (800, height)) esoldier = {"Health": 100} print(width, height) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: pygame.quit() sys.exit() if event.key == pygame.K_h: Player.player["Health"] -= 1 #Backround screen.blit(backround,(0,0)) #Enemies screen.blit(Enemies.esoldier_surf, Enemies.esoldier_rect) #Player healthnum = Player.player["Health"] healthnums = [int(x) for x in str(healthnum)] Player.player_gravity += 1 Player.player_rect.y += Player.player_gravity if Player.player_rect.bottom >= height: Player.player_rect.bottom = height keys = pygame.key.get_pressed() if keys[pygame.K_d] == 1: if keys[pygame.K_LCTRL] == 1: Player.player_rect.x += 9 else: Player.player_rect.x += 5 if keys[pygame.K_a] == 1: if keys[pygame.K_LCTRL] == 1: Player.player_rect.x -= 9 else: Player.player_rect.x -= 5 if keys[pygame.K_SPACE] == 1 and Player.player_rect.bottom >= height: Player.player_gravity = -20 gunxposmdl = Player.player_rect.right gunyposmdl = Player.player_rect.top gunyposmdl += 20 mouse_x, mouse_y = pygame.mouse.get_pos() rel_x, rel_y = mouse_x - gunxposmdl, mouse_y - gunyposmdl angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) if angle > 90 or angle < -90: if angle > 0: angle =-abs(angle) else: angle = abs(angle) rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle)) rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl)) player_surf_cpy = Player.player_surf.copy() player_fliped = pygame.transform.flip(player_surf_cpy, False, True) flipgunxposmdl = Player.player_rect.left rshotgunmdlcpy = rshotgunmdl.copy() rshotgunmdlfliped = pygame.transform.flip(rshotgunmdlcpy, False, True ) rshotgunrectfliped = rshotgunmdlfliped.get_rect(midright=(flipgunxposmdl, gunyposmdl)) screen.blit(player_fliped, Player.player_rect) screen.blit(rshotgunmdlfliped, rshotgunrectfliped) sg_muzzle_x, sg_muzzle_y = rshotgunrectfliped.midleft[0], rshotgunrectfliped.midleft[1] else: rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle)) rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl)) screen.blit(Player.player_surf, Player.player_rect) screen.blit(rshotgunmdl, rshotgunrect) sg_muzzle_x, sg_muzzle_y = rshotgunrect.midright[0], rshotgunrect.midright[1] #Cast rays pygame.draw.line(screen, ("Green"), (sg_muzzle_x, sg_muzzle_y), (sg_muzzle_x, sg_muzzle_y), 3) #UI mpos = pygame.mouse.get_pos() sbarxpos = width - 600 sbarxpos = sbarxpos / 2 sbarypos = height - 46 screen.blit(sbar,(sbarxpos,sbarypos)) if len(healthnums) == 3: hnum1 = pygame.image.load("textures/UI/HUD/NUM_1.png").convert_alpha() hnum2 = pygame.image.load("textures/UI/HUD/NUM_0.png").convert_alpha() hnum3 = pygame.image.load("textures/UI/HUD/NUM_0.png").convert_alpha() if width == 1366: screen.blit(hnum1,(645,727)) screen.blit(hnum2,(685,727)) screen.blit(hnum3,(727,727)) elif width == 1920: numypos = height - 44 #screen.blit(hnum1,(, numypos)) #screen.blit(hnum2,(, numypos)) #screen.blit(hnum3,(, numypos)) if len(healthnums) == 2: numa = healthnums[0] numb = healthnums[1] hnum2 = pygame.image.load(f"textures/UI/HUD/NUM_{numa}.png").convert_alpha() hnum3 = pygame.image.load(f"textures/UI/HUD/NUM_{numb}.png").convert_alpha() if width == 1366: screen.blit(hnum2,(685,727)) screen.blit(hnum3,(727,727)) elif width == 1920: numypos = height - 44 if len(healthnums) == 1: numb = healthnums[0] hnum3 = pygame.image.load(f"textures/UI/HUD/NUM_{numb}.png").convert_alpha() screen.blit(hnum3,(727,727)) screen.blit(mousec, (mpos)) pygame.display.update() clock.tick(60) Link to Source files: https://www.dropbox.com/s/4tuk7v675go1urv/2DQuake.zip?dl=0
Python PyGame append object on the screen multiple times with different movement
I am trying to make an object with very random movement which is fine, that works. But if I want the same object to append to the screen 10 times then it does not work or i dont know how to do that. The object is called ant. I am sorry if its a bad, unreadable code im still a beginner in pygame. So the ant object(which is a small red square), need to be visible on the screen 10 times, each ant need to move randomly. I appreciate all your answers thanks. import pygame as pg import random as rd import math import sys from pygame.time import Clock pg.init() screen = pg.display.set_mode((500,500)) title = pg.display.set_caption("TEST") #REDRAWING SCREEN def redraw(): screen.fill((0,0,0)) pg.display.update() #FOOD def food(): food_x = 200 food_y = 200 FOOD_COLOR = (255,255,255,0) FWIDTH = 15 fooddraw = pg.draw.circle(screen, FOOD_COLOR, (food_x, food_y), FWIDTH) #ANT VARIABLES ant_x = 200 ant_y = 200 COLOR = (220,20,60,0) ANT_WIDTH = 5 #RANDOM MOVES USING VECTOR def move(): global ant_x,ant_y x_boundary = 1000 y_boundary = 1000 pos = pg.math.Vector2(ant_x, ant_y) ant_x = rd.randrange(0, x_boundary) ant_y = rd.randrange(0, y_boundary) speed = 1 maxdist = 1 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 2).rotate(rd.randrange(360)) pos += direction * speed dist -= speed ant_x, ant_y = round(pos.x), round(pos.y) if dist <= 0: dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_x >= screen.get_width(): #IF ANTS X IS OFF THE SCREEN WIDTH THEN RESET THE X POSITION ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_y >= screen.get_height(): #RESET POSITION IF ANTS Y IS OFF THE SCREEN HEIGHT ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass #TRIED TO DUPLICATE ANT 10 TIMES WITH DIFFERENT MOVES BUT DOESNT WORK, I STILL SEE ONLY 1 ANT ON THE SCREEN ant_spawn = [] for i in range(10): for j in range(2): ant_x = 250 ant_y = 250 ant_spawn.append(j) while True: FPS = pg.time.Clock() redraw() for event in pg.event.get(): if event.type == pg.QUIT: pg.quit() #APPEND TO SCREEN for ants in ant_spawn: antdraw = pg.draw.rect(screen, COLOR, (ant_x, ant_y,3,3)) #MOVE FUNCTION ACTIVATE move() pg.display.update() FPS.tick(60)
You have to create a list of coordinates instead a list of indices: ant_spawn = [] for i in range(10): ant_x = 250 ant_y = 250 ant_spawn.append([ant_x, ant_y]) Move and draw the ants in loops: for ants in ant_spawn: move(ants) for ants in ant_spawn: pg.draw.rect(screen, COLOR, (*ants, 3, 3)) The move function changes the coordinates of an ant: def move(ants): ant_x, ant_y = ants # change ant_x and ant_y # [...] ants[0] = ant_x ants[1] = ant_y Complete example (start with space bar) import pygame as pg import random as rd pg.init() screen = pg.display.set_mode((500,500)) title = pg.display.set_caption("TEST") ant_x = 200 ant_y = 200 COLOR = (220,20,60,0) ANT_WIDTH = 5 def move(ants): ant_x, ant_y = ants x_boundary = 1000 y_boundary = 1000 pos = pg.math.Vector2(ant_x, ant_y) c = rd.randrange(0, x_boundary) ant_y = rd.randrange(0, y_boundary) speed = 1 maxdist = 1 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 2).rotate(rd.randrange(360)) pos += direction * speed dist -= speed ant_x, ant_y = round(pos.x), round(pos.y) if dist <= 0: dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_x >= screen.get_width(): #IF ANTS X IS OFF THE SCREEN WIDTH THEN RESET THE X POSITION ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_y >= screen.get_height(): #RESET POSITION IF ANTS Y IS OFF THE SCREEN HEIGHT ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass ants[0] = ant_x ants[1] = ant_y ant_spawn = [] for i in range(10): ant_x = 250 ant_y = 250 ant_spawn.append([ant_x, ant_y]) move_ants = False run = True while run: FPS = pg.time.Clock() for event in pg.event.get(): if event.type == pg.QUIT: run = False if event.type == pg.KEYDOWN and event.key == pg.K_SPACE: move_ants = not move_ants if move_ants: for ants in ant_spawn: ants = move(ants) screen.fill((0,0,0)) for ants in ant_spawn: #pg.draw.rect(screen, COLOR, (*ants, 3, 3)) pg.draw.circle(screen, COLOR, ants, 5) pg.display.update() FPS.tick(60) pg.quit()
How to rotate pymunk joints at will?
I'm trying to create a walking spider like this: I considered using a SimpleMotor at the pink and red joints and control them using the rate function. But when I tried, I get an error that the function is not callable. self.motorJoint1.rate(0.0) TypeError: 'float' object is not callable I don't see any other functions in the pymunk API that allow controlling the joints at will. Is there really no function or am I missing something? Basically in the run loop I want to specify rotations to the joints at certain points of time, to not just make the spider walk, but to eventually be able to use Neural Networks to allow it to experiment with various configurations of leg positions and figure out which ones can make it walk: angle1 = 30 angle2 = 10 redJoint1.rotate(angle1) pinkJoint2.rotate(angle2) if angle1 < 50: angle1 = angle1 + 1 Is it possible at all to achieve such a level of control over joints using Pymunk? To be able to stop moving the legs (without needing to put the body to sleep), or to rotate the leg joints to whatever angle the spider 'wishes to' at any point in time? Sample code would be a great help.
From the servo example I took a hint and implemented this basic leg: import sys import pygame from pygame.locals import USEREVENT, QUIT, KEYDOWN, KEYUP, K_s, K_r, K_q, K_ESCAPE, K_UP, K_DOWN, K_RIGHT, K_LEFT from pygame.color import THECOLORS import pymunk from pymunk import Vec2d import pymunk.pygame_util class Simulator(object): def __init__(self): self.display_flags = 0 self.display_size = (600, 600) self.space = pymunk.Space() self.space.gravity = (0.0, -1900.0) #self.space.damping = 0.999 # to prevent it from blowing up. # Pymunk physics coordinates start from the lower right-hand corner of the screen. self.ground_y = 100 ground = pymunk.Segment(self.space.static_body, (5, self.ground_y), (595, self.ground_y), 1.0) ground.friction = 1.0 self.space.add(ground) self.screen = None self.draw_options = None def reset_bodies(self): for body in self.space.bodies: if not hasattr(body, 'start_position'): continue body.position = Vec2d(body.start_position) body.force = 0, 0 body.torque = 0 body.velocity = 0, 0 body.angular_velocity = 0 body.angle = body.start_angle def draw(self): self.screen.fill(THECOLORS["white"])### Clear the screen self.space.debug_draw(self.draw_options)### Draw space pygame.display.flip()### All done, lets flip the display def main(self): pygame.init() self.screen = pygame.display.set_mode(self.display_size, self.display_flags) width, height = self.screen.get_size() self.draw_options = pymunk.pygame_util.DrawOptions(self.screen) def to_pygame(p): return int(p.x), int(-p.y+height) #Small hack to convert pymunk to pygame coordinates def from_pygame(p): return to_pygame(p) clock = pygame.time.Clock() running = True font = pygame.font.Font(None, 16) # Create the spider chassisXY = Vec2d(self.display_size[0]/2, self.ground_y+100) chWd = 70; chHt = 50 chassisMass = 10 legWd_a = 50; legHt_a = 5 legWd_b = 100; legHt_b = 5 legMass = 1 relativeAnguVel = 0 #---chassis chassis_b = pymunk.Body(chassisMass, pymunk.moment_for_box(chassisMass, (chWd, chHt))) chassis_b.position = chassisXY chassis_shape = pymunk.Poly.create_box(chassis_b, (chWd, chHt)) chassis_shape.color = 200, 200, 200, 100 print("chassis position");print(chassis_b.position) #---first left leg a leftLeg_1a_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_a, legHt_a))) leftLeg_1a_body.position = chassisXY - ((chWd/2)+(legWd_a/2), 0) leftLeg_1a_shape = pymunk.Poly.create_box(leftLeg_1a_body, (legWd_a, legHt_a)) leftLeg_1a_shape.color = 255, 0, 0, 100 #---first left leg b leftLeg_1b_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_b, legHt_b))) leftLeg_1b_body.position = leftLeg_1a_body.position - ((legWd_a/2)+(legWd_b/2), 0) leftLeg_1b_shape = pymunk.Poly.create_box(leftLeg_1b_body, (legWd_b, legHt_b)) leftLeg_1b_shape.color = 0, 255, 0, 100 #---first right leg a rightLeg_1a_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_a, legHt_a))) rightLeg_1a_body.position = chassisXY + ((chWd/2)+(legWd_a/2), 0) rightLeg_1a_shape = pymunk.Poly.create_box(rightLeg_1a_body, (legWd_a, legHt_a)) rightLeg_1a_shape.color = 255, 0, 0, 100 #---first right leg b rightLeg_1b_body = pymunk.Body(legMass, pymunk.moment_for_box(legMass, (legWd_b, legHt_b))) rightLeg_1b_body.position = rightLeg_1a_body.position + ((legWd_a/2)+(legWd_b/2), 0) rightLeg_1b_shape = pymunk.Poly.create_box(rightLeg_1b_body, (legWd_b, legHt_b)) rightLeg_1b_shape.color = 0, 255, 0, 100 #---link left leg b with left leg a pj_ba1left = pymunk.PinJoint(leftLeg_1b_body, leftLeg_1a_body, (legWd_b/2,0), (-legWd_a/2,0))#anchor point coordinates are wrt the body; not the space motor_ba1Left = pymunk.SimpleMotor(leftLeg_1b_body, leftLeg_1a_body, relativeAnguVel) #---link left leg a with chassis pj_ac1left = pymunk.PinJoint(leftLeg_1a_body, chassis_b, (legWd_a/2,0), (-chWd/2, 0)) motor_ac1Left = pymunk.SimpleMotor(leftLeg_1a_body, chassis_b, relativeAnguVel) #---link right leg b with right leg a pj_ba1Right = pymunk.PinJoint(rightLeg_1b_body, rightLeg_1a_body, (-legWd_b/2,0), (legWd_a/2,0))#anchor point coordinates are wrt the body; not the space motor_ba1Right = pymunk.SimpleMotor(rightLeg_1b_body, rightLeg_1a_body, relativeAnguVel) #---link right leg a with chassis pj_ac1Right = pymunk.PinJoint(rightLeg_1a_body, chassis_b, (-legWd_a/2,0), (chWd/2, 0)) motor_ac1Right = pymunk.SimpleMotor(rightLeg_1a_body, chassis_b, relativeAnguVel) self.space.add(chassis_b, chassis_shape) self.space.add(leftLeg_1a_body, leftLeg_1a_shape, rightLeg_1a_body, rightLeg_1a_shape) self.space.add(leftLeg_1b_body, leftLeg_1b_shape, rightLeg_1b_body, rightLeg_1b_shape) self.space.add(pj_ba1left, motor_ba1Left, pj_ac1left, motor_ac1Left) self.space.add(pj_ba1Right, motor_ba1Right, pj_ac1Right, motor_ac1Right) #---prevent collisions with ShapeFilter shape_filter = pymunk.ShapeFilter(group=1) chassis_shape.filter = shape_filter leftLeg_1a_shape.filter = shape_filter rightLeg_1a_shape.filter = shape_filter leftLeg_1b_shape.filter = shape_filter rightLeg_1b_shape.filter = shape_filter simulate = False rotationRate = 2 while running: for event in pygame.event.get(): if event.type == QUIT or (event.type == KEYDOWN and event.key in (K_q, K_ESCAPE)): #running = False sys.exit(0) elif event.type == KEYDOWN and event.key == K_s: # Start/stop simulation. simulate = not simulate elif event.type == KEYDOWN and event.key == K_r: # Reset. # simulate = False self.reset_bodies() elif event.type == KEYDOWN and event.key == K_UP: motor_ba1Left.rate = rotationRate elif event.type == KEYDOWN and event.key == K_DOWN: motor_ba1Left.rate = -rotationRate elif event.type == KEYDOWN and event.key == K_LEFT: motor_ac1Left.rate = rotationRate elif event.type == KEYDOWN and event.key == K_RIGHT: motor_ac1Left.rate = -rotationRate elif event.type == KEYUP: motor_ba1Left.rate = 0 motor_ac1Left.rate = 0 self.draw() ### Update physics fps = 50 iterations = 25 dt = 1.0/float(fps)/float(iterations) if simulate: for x in range(iterations): # 10 iterations to get a more stable simulation self.space.step(dt) pygame.display.flip() clock.tick(fps) if __name__ == '__main__': sim = Simulator() sim.main() It can be controlled with the up, left, right and down arrow keys after first pressing the s key to start the simulation. I've also made sure the variables are created properly linked with each other and named well. The part about making the joints move to a desired angle is yet to be implemented, but perhaps that could be calculated by taking the x,y positions of the ends of the joints and using a formula to calculate the angle and then move the motor until it reaches a desired angle. If there's a better way, do let me know by posting an answer or editing this one.
I get an incorrect position from .get_rect()?
I'm currently working on a school project where I'm making a "hexcells" similar game in pygame and now I'm trying to blit an a new image if the user has clicked a current image. It will blit an image in the top left area, if clicked in the top left area, but not if I click any of the existing images. I told the program to print the coordinates from the images with help of the .get_rect() function, but it remains the same whereever I click and the coordinates aren't even where a image is. Can someone help me understand how this works and help me blit the new images on top of the existing images? Code below is not the entire document, however there is so much garbage/trash/unused code so I'd thought I spare you the time of looking at irrelevant code. Also sorry if the formatting is wrong or the information isn't enough, I tried my best. import pygame, sys from pygame.locals import * #Magic numbers fps = 30 winW = 640 winH = 480 boxSize = 40 gapSize = 75 boardW = 3 boardH = 3 xMargin = int((winW - (boardW * (boxSize + gapSize))) / 2) yMargin = int((winW - (boardW * (boxSize + gapSize))) / 2) #Lil bit o' color R G B NAVYBLUE = ( 60, 60, 100) correctCords = [[175,275,375],[375,275,175]] bgColor = NAVYBLUE unC = pygame.image.load("unC.png") cor = pygame.image.load("correct.png") inc = pygame.image.load("wrong.png") correct = "Correct" inCorrect = "Incorrect" def main(): global FPSCLOCK, DISPLAYSURF pygame.init() FPSCLOCK = pygame.time.Clock() DISPLAYSURF = pygame.display.set_mode((winW, winH)) mousex = 0 #stores x-coordinate of mouse event mousey = 0 #stores y-coordinate of mouse event pygame.display.set_caption("Branches") DISPLAYSURF.fill(bgColor) gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize) while True: mouseClicked = False for event in pygame.event.get(): if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE): pygame.quit() sys.exit() elif event.type == MOUSEMOTION: mousex,mousey = event.pos elif event.type == MOUSEBUTTONUP: mousex, mousey = event.pos pos = pygame.mouse.get_pos() mouseClicked = True unCa = unC.get_rect() corA = cor.get_rect() print unCa print corA print pos if unCa.collidepoint(pos): DISPLAYSURF.blit(cor,(mousey,mousex)) """lada = unC.get_rect() lada = if mousex and mousey == lada: for x in correctCords: for y in x: for z in x: if mousey and mousex == z and y: DISPLAYSURF.blit(cor,(mousey,mousex)) print lada""" pygame.display.update() FPSCLOCK.tick(fps) def gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize): grid = [] cordX = [] cordY = [] correctRecs = [] #cordinates = [] #cordinates.append([]) #cordinates.append([]) #cordinates.append([]) #this is basically getBoard() all over again #This part will arrange the actual backend grid for row in range(3): grid.append([]) #cordinates[0].append(gapSize+(row+1)*100) #cordinates[1].append(gapSize+(row+1)*100) #cordinates[2].append(gapSize+(row+1)*100) for column in range(3): grid[row].append(inCorrect) for row in range(3): cordX.append([]) for column in range(3): cordX[row].append(gapSize+(row+1)*100) for row in range(3): cordY.append([]) for column in range(3): cordY[row].append(gapSize+(column+1)*100) #print cordX[0][0], cordY[0][0] grid[0][2] = correct grid[1][1] = correct grid[2][0] = correct #Y-AXEL SKRIVS FoRST ([Y][X]) #print cordinates[2][1] DISPLAYSURF.blit(cor,(100,100)) #Let's draw it as well for row in range(3): for column in range(3): DISPLAYSURF.blit(unC,(gapSize+(row+1)*100,gapSize+(column+1)*100)) main() Also real sorry about the horrible variable naming and occasional swedish comments.
unCa = unC.get_rect() gives you only image size - so use it only once at start (before while True) - and later use the same unCa all the time to keep image position and change it. btw: better use more readable names - like unC_rect ie. # move 10 pixel to the right unC_rect.x += 10 # set new position unC_rect.x = 10 unC_rect.right = 100 unC_rect.topleft = (10, 200) unC_rect.center = (10, 200) # center on screen unC_rect.center = DISPLAYSURF.get_rect().center etc. And then use this rect to blit image blit(unC, unC_rect) and check collision with other rect if unC_rect.colliderect(other_rect): or with point - like mouse position elif event.type == MOUSEMOTION: if unC_rect.collidepoint(pygame.mouse.get_pos()): hover = True # shorter elif event.type == MOUSEMOTION: hover = unC_rect.collidepoint(pygame.mouse.get_pos()):
Pygame: Colliding Rectangle on multiple other rectangles
I am attempting to create a game in which a block moves back and forth until the player presses space. Upon which, the block jumps to the next line up and stops. Currently i am having problems with the collision code. The error being thrown up by the shell is: if doRectsOverlap(j['rect'], floors['line']): TypeError: list indices must be integers, not str I am stuck with understanding where my code has gone wrong. My knowledge of how python works is very limited. There is also code i have commented out to do with the floor moving dowards when the player jumps. it has been commented out until i can get the collisions working, but still included Code Below: import pygame, sys, time from pygame.locals import * def doRectsOverlap(rect1, rect2): for a, b in [(rect1, rect2), (rect2, rect1)]: # Check if a's corners are inside b if ((isPointInsideRect(a.left, a.top, b)) or (isPointInsideRect(a.left, a.bottom, b)) or (isPointInsideRect(a.right, a.top, b)) or (isPointInsideRect(a.right, a.bottom, b))): return True return False def isPointInsideRect(x, y, rect): if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y < rect.bottom): return True else: return False # set up pygame pygame.init() mainClock = pygame.time.Clock() # set up the window WINDOWWIDTH = 480 WINDOWHEIGHT = 800 windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32) pygame.display.set_caption('Jumper') #Directions LEFT = 4 RIGHT = 6 UP = 8 DOWN = 2 STILL = 5 #blocks location for jumping #BLOCKLOCY = 700 #Binary for stopping movement #STOPPER = 0 MOVESPEED = 1 # set up the colors BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) j = {'rect':pygame.Rect(240, 700, 20, 20), 'color':GREEN, 'dir':LEFT, 'jump':STILL} f1 = {'line':pygame.Rect(0,720,480,2), 'color':GREEN, 'dir':STILL} f2 = {'line':pygame.Rect(0,650,480,2), 'color':GREEN, 'dir':STILL} floors = [f1,f2] # run the game loop while True: # check for the QUIT event for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() # draw the black background onto the surface windowSurface.fill(BLACK) # move the block data structure if j['dir'] == LEFT: j['rect'].left -= MOVESPEED if j['dir'] == RIGHT: j['rect'].left += MOVESPEED if j['jump'] == UP: j['rect'].bottom -= MOVESPEED #BLOCKLOCY -= MOVESPEED if j['rect'].left < 0: j['dir'] = RIGHT if j['rect'].left > WINDOWWIDTH-j['rect'].width: j['dir'] = LEFT if event.type == KEYDOWN: if event.key == K_SPACE: j['jump'] = UP if doRectsOverlap(j['rect'], floors['line']): j['jump'] = STILL #Floor controll code for moving level - not working currently # for f in floors: #if f['dir'] == DOWN: # f['line'].y += MOVESPEED # if event.type == KEYDOWN: # if event.key == K_SPACE: # f['dir'] = DOWN # if f['line'].top == BLOCKLOCY: # f['dir'] = STILL # STOPPER = 1 #if f['line'].bottom == BLOCKLOCY: # f['dir'] = STILL # STOPPER = 1 # draw the block onto the surface pygame.draw.rect(windowSurface, j['color'], j['rect']) pygame.draw.rect(windowSurface, f['color'], f['line']) # draw the window onto the screen pygame.display.update() mainClock.tick(40)
You are creating floors as a list: f1 = {'line':pygame.Rect(0,720,480,2), 'color':GREEN, 'dir':STILL} f2 = {'line':pygame.Rect(0,650,480,2), 'color':GREEN, 'dir':STILL} floors = [f1,f2] So when you call: if doRectsOverlap(j['rect'], floors['line']): j['jump'] = STILL You're message is telling you that you need an index as an int: for n in range(len(floors)): if doRectsOverlap(j['rect'], floors[n]['line']): j['jump'] = STILL