I am currently working on a small project. I am looking to create a basic RPG to get used to classes in Python 2.7.1. I am using Pygame. I currently coded a movement system which is click based and centred on the player sprite (fixed at the centre of the display). Looking to mimic the Diablo II system.
All the files to run the program can be found at this WeTransfer link: Program Files
Movement Basics: At each iteration of the main loop I run the get_offset(character,event) function below, if the MOUSEBUTTONDOWN event is detected, then and only then does it provides me with a x and y offset and a number of "steps" needed to reach the destination (area of the map under the mouse position at the time of click).
The x and y offsets are repeated at each loop until the player clicks again or that the number of "steps" fall to 0, in which case the offsets are set to 0 (player stops).
The get_offset function is below:
def get_offset(character,event):
if event.type == MOUSEBUTTONDOWN:
print 'reset'
m_pos = pygame.mouse.get_pos() #mouse position tracker
variables.player_dest_pos = m_pos
speed = character.speed #player's speed
xp = (variables.screenWIDTH/2)
yp = (variables.screenHEIGHT/2)
#print xp,yp
xm = m_pos[0]-10 #adds offset to center player
ym = m_pos[1]-5
dx= xm-xp
dy= float(ym-yp)
dist = (dx**2+dy**2)**0.5 #get lenght to travel
#sets number of steps/loops needed to reach destination
steps = dist/speed #float
steps = int(dist)/speed #int
#calculates angle and sets quadrant
if dx == 0 or dy == 0:
angle_rad = 0
if dx == 0 and ym > yp:
xoffset = 0
yoffset = -speed
elif dx == 0 and ym < yp:
xoffset = 0
yoffset = speed
elif dy == 0 and xm > xp:
xoffset = 0
yoffset = -speed
elif dy == 0 and xm < xp:
xoffset = 0
yoffset = speed
else:
xoffset, yoffset = 0,0
elif xm > xp and ym > yp:
angle_rad = np.arctan((abs(dy)/abs(dx)))
xoffset = -np.cos(angle_rad)*speed
yoffset = -np.sin(angle_rad)*speed
elif xm < xp and ym > yp:
angle_rad = np.arctan((abs(dx)/abs(dy)))
xoffset = -np.cos(angle_rad)*speed
yoffset = -np.sin(angle_rad)*speed
elif xm < xp and ym < yp:
angle_rad = np.arctan((abs(dy)/abs(dx)))
xoffset = np.cos(angle_rad)*speed
yoffset = np.sin(angle_rad)*speed
else:# xm > xp and ym < yp:
angle_rad = np.arctan((abs(dx)/abs(dy)))
xoffset = -np.sin(angle_rad)*speed
yoffset = np.cos(angle_rad)*speed
#sets the values to global accessible in the 'variables' module
variables.xoffset = xoffset
variables.yoffset = yoffset
variables.offset_time = steps
I then create a enemy sprite which has the following move method:
def move(self,event):#,mouse_pos, screen, background
if event.type == MOUSEBUTTONDOWN:
self.dest = (random.randint(0,600),random.randint(0,480))
if self.dest[0] > self.rect[0]:
variables.screen.blit(variables.background, self.rect, self.rect) #erases players by bliting bg
variables.move_speed = self.speed
self.move_EW() #moves player
variables.screen.blit(self.image, self.rect) #draws player
if self.dest[0] < self.rect[0]:
variables.screen.blit(variables.background, self.rect, self.rect) #erases players by bliting bg
variables.move_speed = -self.speed
self.move_EW() #moves player
variables.screen.blit(self.image, self.rect) #draws player
if self.dest[1] < self.rect[1]:
variables.screen.blit(variables.background, self.rect, self.rect) #erases players by bliting bg
variables.move_speed = -self.speed
self.move_NS() #moves player
variables.screen.blit(self.image, self.rect) #draws player
if self.dest[1] > self.rect[1]:
variables.screen.blit(variables.background, self.rect, self.rect) #erases players by bliting bg
variables.move_speed = self.speed
self.move_NS() #moves player
variables.screen.blit(self.image, self.rect) #draws player
def move_NS(self):
self.rect = self.rect.move(0, variables.move_speed)
if self.rect.top < 0:
self.rect.bottom = 480
if self.rect.bottom > 480:
self.rect.top = 0
# Check for Collisions
for obstacle in variables.building_list:
if pygame.sprite.collide_rect(self, obstacle):
#print 'collide'
self.rect = self.rect.move(0, -(variables.move_speed))
def move_EW(self):
self.rect = self.rect.move(variables.move_speed , 0)
if self.rect.right > 600:
self.rect.left = 0
if self.rect.left < 0:
self.rect.left = 600
# Check for Collisions
for obstacle in variables.building_list:
if pygame.sprite.collide_rect(self, obstacle):
#print 'collide'
self.rect = self.rect.move(-(variables.move_speed) , 0)
The movement of this sprite is basically independent of anything else at the moment.
I then coded in a group_offset(group,character) function which is able to add the get_offset offsets to any sprite in a group, in order to make any sprite in the game "move" relatively to the player (this therefore applies also to static objects.) It also checks if any elements of the group collide with the player using a local variable test_rect, if collision occurs then the offset is set to 0 in both direction. If no collision occurs, then the sprite is moved by the offset value.
def group_offset(group,character):
for sprite in group: #checks if sprite collide with character using test_rect
test_rect = Rect(sprite.rect)
if variables.offset_time > 0:
test_rect = test_rect.move(variables.xoffset,variables.yoffset)
if test_rect.colliderect(character.rect):
variables.xoffset, variables.yoffset = 0,0
#if no collision occurs moves the sprites in the group
if variables.xoffset != 0 and variables.yoffset != 0:
for sprite in group:
if variables.offset_time > 0:
sprite.rect = sprite.rect.move(variables.xoffset,variables.yoffset)
My problem occurs when I use the group offset function twice, once for my buildings and once for my enemies. When I do that my hero stops moving after what looks like only one "step" has been taken.
After the first call all is fine and the hero moves until it reaches it's destination.
When I call it again with group_offset(variables.ennemi_list,hero). Then my player stops after one step.
I initially thought that my enemy sprites where checking collision against my buildings instead of my player, but as you can see the call is made with the player(hero) so it should only detect a collision and stop the movement either when my player hit's a building (from the first call) or an enemy...
Any help would be helpful as I really don't know why this bug is occurring. I've tried commenting out many things and it really seems like the problem comes from this line of code: group_offset(variables.ennemi_list,hero).
This is my main.py script with the game loop:
import pygame, sys, variables from pygame.locals import * from classes import * from instances import * from functions import *
#game loop
while True:
variables.screen.fill((0,0,0)) #make background black for map edges
for event in pygame.event.get(): #setting up quit
if event.type == QUIT:
pygame.quit()
sys.exit()
get_offset(hero,event) # sets the movement offset for the iteration
for o in variables.char_list:
if isinstance(o, Character): #moves characters
o.move(event)
if isinstance(o, Ennemi): #performs attack when enemmi is clicked and in contact
if pygame.sprite.collide_rect(o,hero) == True:
hero.attack(sword,o)
group_offset(variables.building_list,hero) #new building position using offset
scroll_map.offset() #offsets grass background map
variables.screen.blit(scroll_map.image, scroll_map.rect) # blits the grass map to new pos
variables.building_list.draw(variables.screen) #blits the buildings to new pos
variables.screen.blit(hero.image, hero.rect) #blits hero to screen center
#group_offset(variables.ennemi_list,hero)
for o in variables.ennemi_list:
variables.screen.blit(o.image, o.rect)
pygame.display.update()
pygame.time.delay(10)
variables.offset_time -= 1 #removes on step from the offset counter to keep track of when
#offset needs to be reset to 0, i.e. position is reached