Trig is not my strong suit which is why I am having probs trying to get my guided missile to rotate properly. Looks like I have the direction fine although Im still not 100% happy with the movement, so Im hoping by fixing the rotation it will fix that also. Problem is my missiles dont point towards the target (player), and they also flip at a certain point past the target. Heres is the code...the rotations just dont work correctly.
def update(self):
self.calcVect()
self.calcPos()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)
def calcVect(self):
self.vectX = self.targetX - self.x
self.vectY = self.targetY - self.y
self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY))
self.normX = self.vectX/self.length
self.normY = self.vectY/self.length
self.dx += self.normX*self.power
self.dy += self.normY*self.power
def calcPos(self):
self.x += self.dx*0.2
self.y += self.dy*0.2
def rotate(self):
radians = math.atan2(-self.vectY,self.vectX)
radians %= 2*math.pi
self.dir = math.degrees(radians)
print self.dir
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMissile, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
Working Code
Here is the new working code for the entire class. The expression at the end of this line enabled correct rotations "radians = math.atan2(self.vectX, self.vectY)- math.pi/2" even though the image was horizontal to start with.
class GuidedMissile(pygame.sprite.Sprite):
def __init__(self, source):
pygame.sprite.Sprite.__init__(self)
self.screen = source.screen
self.imageMissile = pygame.image.load("Images/missile.tga").convert()
self.imageMissile.set_colorkey((0,0,255))
self.image = self.imageMissile
self.rect = self.image.get_rect()
self.rect.center = source.rect.midbottom
self.rect.inflate_ip(-15, -5)
self.x, self.y = self.rect.center
self.X, self.Y = self.rect.center
self.dx = 0
self.dy = 0
self.power = 3
self.dir = 0
def update(self):
self.player = goodSpritesOneGRP.sprite
self.targetX, self.targetY = self.player.rect.center
NX, NY = self.calcVect()
self.calcVel(NX, NY)
self.calcPos()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)
def calcVect(self):
self.vectX = self.targetX - self.x
self.vectY = self.targetY - self.y
self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY))
if self.length == 0:
self.normX = 0
self.normY = 1
else:
self.normX = self.vectX/self.length
self.normY = self.vectY/self.length
return (self.normX, self.normY)
def calcVel(self,NX,NY ):
self.dx += NX*self.power
self.dy += NY*self.power
def calcPos(self):
self.x += self.dx*0.05
self.y += self.dy*0.05
def rotate(self):
radians = math.atan2(self.vectX, self.vectY)- math.pi/2
radians %= 2*math.pi
self.dir = math.degrees(radians)
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMissile, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def checkBounds(self):
global guidedMissile
screen = self.screen
if self.x > screen.get_width():
self.kill()
guidedMissile -= 1
if self.x < -100:
self.kill()
guidedMissile -= 1
if self.y > screen.get_height() + 100:
self.kill()
guidedMissile -= 1
if self.y < -100:
self.kill()
guidedMissile -= 1
When you call calcPos() you have to recalculate self.vectX and self.vectY, otherwise the missile will not point to the target. And don't forget to check if self.length == 0
You can fix splitting calcVect():
def calcVect(self):
self.vectX = self.targetX - self.x
self.vectY = self.targetY - self.y
self.length = math.sqrt((self.vectX*self.vectX)+(self.vectY*self.vectY))
if self.length != 0:
normX = vectX/self.length
normY = vectY/self.length
# Update velocity
def calcVel(self):
self.dx += self.normX*self.power
self.dy += self.normY*self.power
Then
def update(self):
self.calcVect()
self.calcVel()
self.calcPos()
self.calcVect()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)
This solution works, but I suggest you to write a generic method calcVectTo(self, target) that returns the unitary vector pointing to (target.x, target.y):
def calcVect(self, target):
vectX = target.x - self.x
vectY = target.y - self.y
length = math.sqrt((vectX*vectX)+(vectY*vectY))
if length == 0:
normX = 0
normY = 1
else
normX = vectX/length
normY = vectY/length
return (normX, normY)
Edit
I did not understand that the target is fixed, now it is much easier: you only need to calculate vect at the construction of the class, moving calcVect from update to __construct (ignore my second implementation of calcVect):
class GuidedMissile(pygame.sprite.Sprite):
def __init__(self, source, target):
...
self.calcVect()
def update(self):
self.calcVel()
self.calcPos()
self.rotate()
self.checkBounds()
self.rect.center = (self.x, self.y)
Related
def movement(self):
prevX = self.x
prevY = self.y
key = pygame.key.get_pressed()
if key[pygame.K_w]:
self.y -= 32
elif key[pygame.K_s]:
self.y += 32
elif key[pygame.K_a]:
self.x -= 32
elif key[pygame.K_d]:
self.x += 32
x = int(self.x / 32)
y = int(self.y / 32)
if TEXT_LEVEL[x][y] != "=":
print("wall")
print(x,y)
print(TEXT_LEVEL[x][y])
self.rect.topleft = (self.x, self.y)
self.x = prevX
self.y = prevY
So I have an array that stores the map as a text file as such:
============.===============
=..........=...............=
=..........=...............=
=...........*..............=
=....===============.......=
=....=.....................=
=....=.............=.......=
.....===============........
=..................*.......=
=..........................=
=....===============.......=
=....*.............*.......=
=........=====.............=
= .........................=
============.===============
And what I'm doing is checking if the current position my sprite is in matches to the coordinates of a wall. If it does I reset the x and y and don't update its location. For some reason, this is not working and I'm stuck as to why it's failing.
The full project can be found on my github if anything needs to be referenced: https://github.com/rob-roibu/Pac-man
You need to update self.rect.topleft after changing self.x and self.y:
self.x = prevX
self.y = prevY
self.rect.topleft = (self.x, self.y)
Im trying to play my code but i get this error 'video system not initialized' and i dont even know where that could be coming from. Here is my code so far:
#Imports
import sys as pygame
import pygame as pg
from random import randint, uniform
vec = pg.math.Vector2
#Player variables
WIDTH = 1200
HEIGHT = 660
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
DARKGRAY = (40, 40, 40)
#----------------------------------------------------------------------------
# Player properties
Player_SIZE = 32
MAX_SPEED = 5
MAX_FORCE = 0.5
APPROACH_RADIUS = 100
all_sprites = pg.sprite.Group()
class Player(pg.sprite.Sprite):
def __init__(self):
self.groups = all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.image = pg.Surface((Player_SIZE, Player_SIZE))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.pos = vec(randint(0, WIDTH), randint(0, HEIGHT))
self.vel = vec(MAX_SPEED, 0).rotate(uniform(0, 360))
self.acc = vec(0, 0)
self.rect.center = self.pos
def follow_mouse(self):
mpos = pg.mouse.get_pos()
self.acc = (mpos - self.pos).normalize() * 0.5
def seek(self, target):
self.desired = (target - self.pos).normalize() * MAX_SPEED
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def seek_with_approach(self, target):
self.desired = (target - self.pos)
dist = self.desired.length()
self.desired.normalize_ip()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def update(self):
# self.follow_mouse()
self.acc = self.seek_with_approach(pg.mouse.get_pos())
# equations of motion
self.vel += self.acc
if self.vel.length() > MAX_SPEED:
self.vel.scale_to_length(MAX_SPEED)
self.pos += self.vel
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
if self.pos.y > HEIGHT:
self.pos.y = 0
if self.pos.y < 0:
self.pos.y = HEIGHT
self.rect.center = self.pos
#Enemy properties
MAX_SPEED1 = 4.5
ppos = pg.mouse.get_pos()
class Enemy(pg.sprite.Sprite):
def __init__(self):
self.groups = all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.image = pg.Surface((Player_SIZE, Player_SIZE))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.pos = vec(randint(0, WIDTH), randint(0, HEIGHT))
self.vel = vec(MAX_SPEED1, 0).rotate(uniform(0, 360))
self.acc = vec(0, 0)
self.rect.center = self.pos
def follow_player(self):
self.acc = (ppos - self.pos).normalize() * 0.5
def seek(self, target):
self.desired = (ppos - self.pos).normalize() * MAX_SPEED1
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def seek_with_approach(self, target):
self.desired = (ppos() - self.pos)
dist = self.desired.length()
self.desired.normalize_ip()
if dist < APPROACH_RADIUS:
self.desired *= dist / APPROACH_RADIUS * MAX_SPEED
else:
self.desired *= MAX_SPEED1
steer = (self.desired - self.vel)
if steer.length() > MAX_FORCE:
steer.scale_to_length(MAX_FORCE)
return steer
def update(self):
# self.follow_mouse()
self.acc = self.seek_with_approach(Player)
# equations of motion
self.vel += self.acc
if self.vel.length() > MAX_SPEED1:
self.vel.scale_to_length(MAX_SPEED1)
self.pos += self.vel
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
if self.pos.y > HEIGHT:
self.pos.y = 0
if self.pos.y < 0:
self.pos.y = HEIGHT
self.rect.center = self.pos
#----------------------------------------------------------------------------
#Display
pg.init()
screen = pg.display.set_mode((WIDTH, HEIGHT))
clock = pg.time.Clock()
pg.display.set_caption("Maze")
Icon = pg.image.load('Logo.png')
pg.display.set_icon(Icon)
#Main loop
all_sprites = pg.sprite.Group()
Player()
Enemy()
paused = False
running = True
while running:
clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
paused = not paused
if event.key == pg.K_m:
Player()
Enemy()
if not paused:
all_sprites.update()
screen.fill(DARKGRAY)
all_sprites.draw(screen)
pg.display.flip()
pg.quit()
I know it's long but Ireally dont know where the problem could be coming from.
Any help? (There is no problem with the indent im only having trouble pasting the code here)
You have to call pygame.init() before you can use pygame.mouse.get_pos(); or, in your case, pg.init(), since you imported pygame as pg.
I don't know why you import sys as pygame; that quite confusing.
Also, getting the mouse position once that the start of your game is not helpful. Everytime you want to know the mouse position, you should call .mouse.get_pos() again.
I am creating an Asteroids type game using Pygame and am having trouble firing projectiles. I have a sprite group for the projectile object and some code to add the projectile.
if pressed[pygame.K_SPACE]:
projectiles.add(Projectile(player.rect.x, player.rect.y, player.direction))
Problem is when I press space in game to fire the projectile I get the error "add() argument after * must be an iterable, not int".
I have a very similar statement for adding asteroids when the game first starts without any issues so I'm not really sure what the problem is. I'll leave the rest of the code below. The add statement giving issues is in the main function near the bottom. Any help is appreciated.
#Import Modules
import pygame
import math
import random
#Movement Function
def calculate_new_xy(old_xy,speed,direction):
new_x = old_xy[0] + (speed*math.cos(direction))
new_y = old_xy[1] + (speed*math.sin(direction))
return new_x, new_y
#Collision Function
def isColliding(x, y, xTo, yTo, size):
if x > xTo - size and x < xTo + size and y > yTo - size and y < yTo + size:
return True
return False
#Draw Text Function
def drawText(msg, color, x, y, s, center=True):
screen_text = pygame.font.SysFont("Impact", s).render(msg, True, color)
if center:
rect = screen_text.get_rect()
rect.center = (x, y-50)
else:
rect = (x, y)
display.blit(screen_text, rect)
#Initialize Variables
#Colors
white = (255, 255, 255)
black = (0, 0, 0)
#Display Height/Width
display_width = 800
display_height = 600
#Asteroid Class
class Asteroid(pygame.sprite.Sprite):
#Initialize values
def __init__(self, pos=(0, 0)):
#Initialize sprite class
pygame.sprite.Sprite.__init__(self)
#Asteroid sprite
self.asteroid = pygame.image.load("asteroid.png").convert()
self.image = self.asteroid
#Rectangle
self.rect = self.image.get_rect()
self.rect.center = pos
#Initialize random starting angle
self.angle = random.randint(0, 360)
#Asteroid random Speed
self.speed = random.randint(2, 3)
#Asteroid random direction
self.direction = math.radians(random.randrange(0, 360, 3))
#Update asteroid object
def update(self):
#Constantly rotate asteroid
self.angle -= 3 % 360
#Get image angle and position
self.image = pygame.transform.rotate(self.asteroid, self.angle*-1)
#Use rectangle to get center of image
#Save ship's current center.
x, y = self.rect.center
#Replace old rect with new rect.
self.rect = self.image.get_rect()
#Put the new rect's center at old center.
self.rect.center = (x, y)
#Move Asteroid
self.rect.center = calculate_new_xy(self.rect.center,self.speed,self.direction)
#Screen Border
#Moves the asteroid to the opposite side of the screen if they go outside the border
if self.rect.x > display_width:
self.rect.x = -20
elif self.rect.x < -20:
self.rect.x = display_width
elif self.rect.y > display_height:
self.rect.y = -20
elif self.rect.y < -20:
self.rect.y = display_height
#Projectile Class
class Projectile(pygame.sprite.Sprite):
#Initialize values
def _init_(self,x,y,direction):
self.x = x
self.y = y
self.dir = direction
self.ttl = 30
#Update projectile object
def update(self):
#Changing direction
self.x += projectilespd * math.cos(self.direction)
self.y += projectilespd * math.sin(self.direction)
#Draw projectile
pygame.draw.circle(display, white, (self.x,self.y),1)
#Screen Border
if self.x > display_width:
self.x = 0
elif self.x < 0:
self.x = display_width
elif self.y > display_height:
self.y = 0
elif self.y < 0:
self.y = display_height
self.ttl -= 1
#Player Class
class Player(pygame.sprite.Sprite):
#Initialize ship sprite, angle lines, and rectangle
def __init__(self, pos=(0, 0), size=(200, 200)):
#Player sprite
self.ship = pygame.image.load("ship.png").convert()
self.image = self.ship
#Rectangle
self.rect = self.image.get_rect()
self.rect.center = pos
#Initialize angle
self.angle = 0
#Initialize direction
self.direction = 0
#Update player object
def update(self):
#Rotation
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: self.angle -= 3 % 360
if pressed[pygame.K_RIGHT]: self.angle += 3 % 360
#Get image angle and position
self.image = pygame.transform.rotate(self.ship, self.angle*-1)
#Use rectangle to get center of image
#Save ship's current center.
x, y = self.rect.center
#Replace old rect with new rect.
self.rect = self.image.get_rect()
#Put the new rect's center at old center.
self.rect.center = (x, y)
#Convert angle to radians
self.direction = math.radians(self.angle-90)
#Increase speed if Up is pressed
if pressed[pygame.K_UP]: self.speed = 5
else: self.speed = 0
#Move Ship
self.rect.center = calculate_new_xy(self.rect.center,self.speed,self.direction)
#Screen Border
#Moves the player to the opposite side of the screen if they go outside the border
if self.rect.x > display_width:
self.rect.x = -50
elif self.rect.x < -50:
self.rect.x = display_width
elif self.rect.y > display_height:
self.rect.y = -50
elif self.rect.y < -50:
self.rect.y = display_height
#Main Function
def main(gameState):
#Player starting position
player = Player(pos=(400, 300))
#Asteroid group
asteroids = pygame.sprite.Group()
#Projectile group
projectiles = pygame.sprite.Group()
#Create asteroids
for x in range(8):
asteroids.add(Asteroid(pos=(100 + (x*120), 100 + (x*20))))
while True:
for event in pygame.event.get():
#closes game
if event.type == pygame.QUIT:
done = True
pygame.quit()
exit()
#Game Menu
while gameState == "Menu":
#Fill background
display.fill((0,0,0))
#Display menu text
drawText("ASTEROIDS", white, display_width / 2, display_height / 2, 150)
drawText("Press any key to START", white, display_width / 2, display_height / 2 + 120, 40)
#Check game start or end
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
gameState = "Playing"
pygame.display.update()
#Low frame rate for menu
clock.tick(5)
#Get key inputs
pressed = pygame.key.get_pressed()
#Fill background
display.fill(black)
#Check for player collision with asteroid
for asteroid in asteroids:
if gameState != "Game Over":
if isColliding(player.rect.x, player.rect.y, asteroid.rect.x, asteroid.rect.y, 30):
gameState = "Game Over"
#Update and draw player if not game over
if gameState != "Game Over":
#Update player
player.update()
#Draw player
display.blit(player.image, player.rect)
#Update asteroids
asteroids.update()
#Draw asteroids
asteroids.draw(display)
#Fire Projectiles
if pressed[pygame.K_SPACE]:
projectiles.add(Projectile(player.rect.x, player.rect.y, player.direction))
#Update projectiles
projectiles.update()
#Draw projectiles
projectiles.draw(display)
#Display Game Over and restart option
if gameState == "Game Over":
drawText("GAME OVER", white, display_width / 2, display_height / 2, 150)
drawText("Press R to restart", white, display_width / 2, display_height / 2 + 120, 40)
if pressed[pygame.K_r]:
main(gameState = "Playing")
#Makes updates to the game screen
pygame.display.update()
#Frame rate
clock.tick(60)
#Initialize Game
if __name__ == '__main__':
#initialize pygame
pygame.init()
#initialize display settings
display = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Asteroids')
#initialize game clock
clock = pygame.time.Clock()
#start main function
main(gameState = "Menu")
The issue is not the pygame.sprite.Group.add operation, but the obejct you want to add is not a pygame.sprite.Sprite object, because the object is not constructed at all.
You missed to the super call in the constructor of Projectile. Furthermore the name of the constructor has to be __init__ rather _init_:
class Projectile(pygame.sprite.Sprite):
#Initialize values
def __init__(self,x,y,direction):
super.__init__()
self.x = x
self.y = y
self.dir = direction
self.ttl = 30
Hello i am making a graphic adventure/rpg with pygame.
Is there a way to make NPCs with pygame, and be able to interact with them, like having a dialog ?
I've been searching on the internet but i didn't have useful results. It would be great if someone could help me.
Here is the main code.
import pygame as pg
import sys
from os import path
from settings import *
from sprites import *
from tiledmap import *
from pgu import gui
from pygame.draw import circle
import pygame_ai as pai
import time
class Game:
def __init__(self):
pg.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.load_data()
def load_data(self):
game_folder = path.dirname(__file__)
img_folder = path.join(game_folder, 'img')
map_folder = path.join(game_folder, 'maps')
self.map = TiledMap(path.join(map_folder, 'mapa_inici.tmx'))
self.map_img = self.map.make_map()
self.map_rect = self.map_img.get_rect()
self.player_img = pg.image.load(path.join(img_folder, PLAYER_IMG)).convert_alpha()
def new(self):
# iniciar totes les variables i fer tota la preparació per a una nova partida
self.all_sprites = pg.sprite.Group()
self.walls = pg.sprite.Group()
#for row, tiles in enumerate(self.map.data):
#for col, tile in enumerate(tiles):
#if tile == '1':
#Wall(self, col, row)
#if tile == 's':
#self.player = Player(self, col, row)
for tile_object in self.map.tmxdata.objects:
if tile_object.name == 'Jugador':
self.player = Player(self, tile_object.x, tile_object.y)
if tile_object.name == 'Muro':
Obstacle(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
self.camera = Camera(self.map.width, self.map.height)
def run(self):
# bucle del joc - s'iguala self.playing = False per finalitzar el joc
self.playing = True
while self.playing:
self.dt = self.clock.tick(FPS) / 1000
self.events()
self.update()
self.draw()
def quit(self):
pg.quit()
sys.exit()
def update(self):
# update portion of the game loop
self.all_sprites.update()
self.camera.update(self.player)
def draw_grid(self):
for x in range(0, WIDTH, TILESIZE):
pg.draw.line(self.screen, LIGHTGREY, (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, TILESIZE):
pg.draw.line(self.screen, LIGHTGREY, (0, y), (WIDTH, y))
def draw(self):
self.screen.blit(self.map_img, self.camera.apply_rect(self.map_rect))
for sprite in self.all_sprites:
self.screen.blit(sprite.image, self.camera.apply(sprite))
pg.display.flip()
def events(self):
# tots els events
for event in pg.event.get():
if event.type == pg.QUIT:
self.quit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.quit()
g = Game()
while True:
g.new()
g.run()
The sprites code
import pygame as pg
from os import path
import sys
from settings import *
import pygame_ai as pai
from tiledmap import TiledMap
vec = pg.math.Vector2
class Player(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = game.player_img
self.rect = self.image.get_rect()
self.vel = vec(0,0)
self.pos = vec(x, y)
self.rot = 0
def get_keys(self):
self.vel = vec(0,0)
keys = pg.key.get_pressed()
if keys[pg.K_LEFT] or keys[pg.K_a]:
self.vel.x = -PLAYER_SPEED
if keys[pg.K_RIGHT] or keys[pg.K_d]:
self.vel.x = PLAYER_SPEED
if keys[pg.K_UP] or keys[pg.K_w]:
self.vel.y = -PLAYER_SPEED
if keys[pg.K_DOWN] or keys[pg.K_s]:
self.vel.y = PLAYER_SPEED
if self.vel.x != 0 and self.vel.y != 0:
self.vel *= 0.7071
def collide_walls(self,dir):
if dir == 'x':
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vel.x > 0:
self.pos.x = hits[0].rect.left - self.rect.width
if self.vel.x < 0:
self.pos.x = hits[0].rect.right
self.vel.x = 0
self.rect.x = self.pos.x
if dir == 'y':
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vel.y > 0:
self.pos.y = hits[0].rect.top - self.rect.height
if self.vel.y < 0:
self.pos.y = hits[0].rect.bottom
self.vel.y = 0
self.rect.y = self.pos.y
def update(self):
self.get_keys()
self.pos += self.vel * self.game.dt
self.rect.x = self.pos.x
self.collide_walls('x')
self.rect.y = self.pos.y
self.collide_walls('y')
class Obstacle(pg.sprite.Sprite):
def __init__(self, game, x, y, w, h):
self.groups = game.walls
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pg.Rect(x, y, w, h)
self.x = x
self.y = y
self.rect.x = x
self.rect.y = y
The tile map code
import pygame as pg
import pytmx
from settings import *
class Map:
def __init__(self, filename):
self.data = []
with open(filename, 'rt') as f:
for line in f:
self.data.append(line.strip())
self.tilewidth = len(self.data[0])
self.tileheight = len(self.data)
self.width = self.tilewidth * TILESIZE
self.height = self.tileheight * TILESIZE
class TiledMap:
def __init__(self, filename):
tm = pytmx.load_pygame(filename, pixelalpha=True)
self.width = tm.width * tm.tilewidth
self.height = tm.height * tm.tileheight
self.tmxdata = tm
def render(self, surface):
ti = self.tmxdata.get_tile_image_by_gid
for layer in self.tmxdata.visible_layers:
if isinstance(layer, pytmx.TiledTileLayer):
for x, y, gid, in layer:
tile = ti(gid)
if tile:
surface.blit(tile, (x * self.tmxdata.tilewidth, y * self.tmxdata.tileheight))
def make_map(self):
temp_surface = pg.Surface((self.width, self.height))
self.render(temp_surface)
return temp_surface
class Camera:
def __init__(self, width, height):
self.camera = pg.Rect(0,0,width,height)
self.width = width
self.height = height
def apply(self, entity):
return entity.rect.move(self.camera.topleft)
def apply_rect(self, rect):
return rect.move(self.camera.topleft)
def update(self, target):
x = -target.rect.centerx + int(WIDTH / 2)
y = -target.rect.centery + int(HEIGHT / 2)
# limit al seguiment del personatge
x = min(0,x) # esquerra
y = min(0,y) # part de dalt
x = max(-(self.width - WIDTH), x) # dreta
y = max(-(self.height - HEIGHT), y) # part de baix
self.camera = pg.Rect(x, y, self.width, self.height)
In the 1960's there was a simple dialogue-like handler named Eliza. Since then there has been many changes and variations, and they have become known as "chat bots". Perhaps your NPCs could have an Eliza-like conversation with the player? It might be worth doing some research on these sort of natural text processors.
However if you mean simply choosing conversation topics from, say an [(a), (b), (c)] type list, these dialogues would typically be mapped out in a graph of choices. Have you read stories where you make choices to change the narrative? These are a simple graph of choices. You could draw them out on a piece of paper then encode them into a data-structure, maybe a python dictionary. Each option moves to another node in the graph. Perhaps they also loop around back upon themselves.
I still have a problem with my software to check collisions between spaceship and asteroids. I have got no idea why I get a collision only in the top left corner of the screen.
any ideas ? any help please ?
import pygame, sys
import random
import math
from pygame.locals import KEYDOWN, K_SPACE
pygame.init()
pygame.display.set_caption("ASTROCRASH version 0.1 >>> DKR103 <<<")
clock = pygame.time.Clock()
SCREENH = 600
SCREENW = 800
SCREEN = pygame.display.set_mode((SCREENW, SCREENH))
sGRAD = math.pi/180
BLACK = (0,0,0)
WHITE = (255,255,255)
BBB = (0, 75, 230)
ASTEROIDS = []
MISSILES = []
SPACESHIPS = []
class AsteroidSprite(pygame.sprite.Sprite):
def __init__(self,posX,posY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/asteroid3.png").convert()
self.rect = self.image.get_rect()
self.x = posX
self.y = posY
self.speed = 2
self.dx = random.choice([1,-1]) * self.speed * random.random()
self.dy = random.choice([1,-1]) * self.speed * random.random()
def update(self):
if self.y > SCREENH:
self.y = (0 - self.rect[3])
if self.y < (0 - self.rect[3]):
self.y = SCREENH
if self.x > SCREENW:
self.x = (0 - self.rect[2])
if self.x < (0 - self.rect[2]):
self.x = SCREENW
self.x += self.dx
self.y += self.dy
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#load original image
self.imageMaster = pygame.image.load("img/spaceship.png")
self.imageMaster = self.imageMaster.convert()
###self.imageMaster.set_colorkey(WHITE)
#set Sprite attribute
self.image = self.imageMaster
#get Sprite rect
self.rect = self.image.get_rect()
self.rect.center = ((SCREEN.get_width()/2),(SCREEN.get_height()/2))
#initial rotation value
self.dir = 0
#ship movement speed
self.speed = 5
def rotation(self):
#set Sprite center before rotation
self.oldCenter = self.rect.center
#rotate Sprite
self.image = pygame.transform.rotate(self.imageMaster,self.dir)
self.rect= self.image.get_rect()
#set new Sprite center equal to old Center so it stays in place
self.rect.center = self.oldCenter
self.value = self.dir * math.pi / 180
def update(self):
#move
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
self.rect[0] -= self.speed * math.sin(self.value)
self.rect[1] -= self.speed * math.cos(self.value)
#rotate
if key[pygame.K_LEFT]:
self.dir += 5
if self.dir > 360:
self.dir = 15
if key[pygame.K_RIGHT]:
self.dir -= 5
if self.dir < 0:
self.dir = 355
#outside SCREEN conditions
if self.rect[1] > SCREENH:
self.rect[1] = (0 - self.rect[3])
if self.rect[1] < (0 - self.rect[3]):
self.rect[1] = SCREENH
if self.rect[0] > SCREENW:
self.rect[0] = (0 - self.rect[2])
if self.rect[0] < (0 - self.rect[2]):
self.rect[0] = SCREENW
def draw(self):
SCREEN.blit(self.image,(self.rect[0],self.rect[1]))
def main():
#spaceship
spaceship = Ship()
SPACESHIPS.append(spaceship)
for i in range(8):
ASTEROIDS.append(AsteroidSprite(300,300))
runGame = True
while runGame:
clock.tick(60)
SCREEN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runGame = False
#update asteroids
for i in range(8):
ASTEROIDS[i].update()
SCREEN.blit(ASTEROIDS[i].image,(ASTEROIDS[i].x,ASTEROIDS[i].y))
for a in ASTEROIDS:
if pygame.sprite.spritecollide(a,SPACESHIPS,0):
SCREEN.fill(BBB)
spaceship.rotation()
spaceship.update()
spaceship.draw()
print spaceship.rect[0]
pygame.display.update()
main()
pygame.quit()
The pygame.sprite.groupcollide() function finds collisions between all sprites of the two passed sprite-groups and returns a dictionary containing the collision information, as the documentation states.
Because the collision is determined by comparing the sprite.rect attribute of each sprite, every time you call an update() method of an sprite instance you need to update the position of self.rect object, instead of changing its self.x and self.y attributes.
Your Ship class is ok, because you change the self.rect object and its own .x or .y attributes. (e.g. self.rect[1] = (0 - self.rect[3])).But in your AsteroidSprite class you create a rect object in the __init__() method and only change the self.x and self.y attributes of an instance when you call the .update() method.
What you need to change:
The .update() method of the AsteroidSprite class, because you need to change the self.rect objects position, which is used for collision detection.
The if statement where you check for a collision, because pygame.sprite.groupcollide() returns a dict object, not a Boolean value.
I hope this helps you a little bit :)
Many thanks. Your answer helped me to solve the problem.
updated code:
import pygame, sys
import random
import math
from pygame.locals import KEYDOWN, K_SPACE
pygame.init()
pygame.display.set_caption("ASTROCRASH version 0.1 >>> DKR103 <<<")
clock = pygame.time.Clock()
SCREENH = 600
SCREENW = 800
SCREEN = pygame.display.set_mode((SCREENW, SCREENH))
sGRAD = math.pi/180
BLACK = (0,0,0)
WHITE = (255,255,255)
BBB = (0, 75, 230)
ASTEROIDS = []
MISSILES = []
SPACESHIPS = []
class AsteroidSprite(pygame.sprite.Sprite):
def __init__(self,posX,posY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("img/asteroid3.png").convert()
self.rect = self.image.get_rect()
self.rect.x = posX
self.rect.y = posY
self.speed = 2
self.dx = random.choice([1,-1]) * self.speed * random.random()
self.dy = random.choice([1,-1]) * self.speed * random.random()
def update(self):
if self.rect.y > SCREENH:
self.rect.y = (0 - self.rect[3])
if self.rect.y < (0 - self.rect[3]):
self.rect.y = SCREENH
if self.rect.x > SCREENW:
self.rect.x = (0 - self.rect[2])
if self.rect.x < (0 - self.rect[2]):
self.rect.x = SCREENW
self.rect.x += self.dx
self.rect.y += self.dy
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#load original image
self.imageMaster = pygame.image.load("img/spaceship.png")
self.imageMaster = self.imageMaster.convert()
###self.imageMaster.set_colorkey(WHITE)
#set Sprite attribute
self.image = self.imageMaster
#get Sprite rect
self.rect = self.image.get_rect()
self.rect.center = ((SCREEN.get_width()/2),(SCREEN.get_height()/2))
#initial rotation value
self.dir = 0
#ship movement speed
self.speed = 5
def rotation(self):
#set Sprite center before rotation
self.oldCenter = self.rect.center
#rotate Sprite
self.image = pygame.transform.rotate(self.imageMaster,self.dir)
self.rect= self.image.get_rect()
#set new Sprite center equal to old Center so it stays in place
self.rect.center = self.oldCenter
self.value = self.dir * math.pi / 180
def update(self):
#move
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
self.rect[0] -= self.speed * math.sin(self.value)
self.rect[1] -= self.speed * math.cos(self.value)
#rotate
if key[pygame.K_LEFT]:
self.dir += 5
if self.dir > 360:
self.dir = 15
if key[pygame.K_RIGHT]:
self.dir -= 5
if self.dir < 0:
self.dir = 355
#outside SCREEN conditions
if self.rect[1] > SCREENH:
self.rect[1] = (0 - self.rect[3])
if self.rect[1] < (0 - self.rect[3]):
self.rect[1] = SCREENH
if self.rect[0] > SCREENW:
self.rect[0] = (0 - self.rect[2])
if self.rect[0] < (0 - self.rect[2]):
self.rect[0] = SCREENW
def draw(self):
SCREEN.blit(self.image,(self.rect[0],self.rect[1]))
def main():
#spaceship
spaceship = Ship()
SPACESHIPS.append(spaceship)
for i in range(8):
ASTEROIDS.append(AsteroidSprite(300,300))
runGame = True
while runGame:
clock.tick(60)
SCREEN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
runGame = False
#update asteroids
for i in range(8):
ASTEROIDS[i].update()
SCREEN.blit(ASTEROIDS[i].image,(ASTEROIDS[i].rect.x,ASTEROIDS[i].rect.y))
for a in ASTEROIDS:
if pygame.sprite.spritecollide(a,SPACESHIPS,0):
SCREEN.fill(BBB)
spaceship.rotation()
spaceship.update()
spaceship.draw()
print spaceship.rect[0]
pygame.display.update()
main()
pygame.quit()