#!/usr/bin/env python # -*- coding: utf-8 -*- #cp1252 import pygame import math import Image import api.Math as CMath from api.Vector import CVector from Mouse import CMouse # TODO: Cambiar las conversiones de grados a radianes y viceversa usando las funciones. # TODO: This class is a rotating sprite. update() in each frame rotates the sprite # image, so this is inneficient for sprites that does not rotate continuoulsy. class CSpriteOLD(pygame.sprite.Sprite): """ An enhanced Sprite class expects a gameEngine.Scene class as its one parameter Use methods to change image, direction, speed Will automatically travel in direction and speed indicated Automatically rotates to point in indicated direction Five kinds of boundary collision """ mPos = None mOffsetX = 0 mOffsetY = 0 mTimeState = 0 def __init__(self): pygame.sprite.Sprite.__init__(self) # self.mGroup = aGroup self.mPos = CVector(0.0, 0.0) self.mVel = CVector(0.0, 0.0) self.mAccel = CVector(0.0, 0.0) # Registration point offset. self.mOffsetX = 0 self.mOffsetY = 0 # Speed of the sprite. self.mSpeed = 0 self.maxSpeed = 50 self.minSpeed = -3 # TODO: Take this values from a constants class. self.setBounds(0, 0, 1200, 900) # Constants for the actions then the sprite reaches a border. #CSprite.WRAP = 0 # Wrap around the edges. #CSprite.BOUNCE = 1 # Bounce off the screen changing direction. #CSprite.STOP = 2 # Stop at the edge of the screen. #CSprite.HIDE = 3 # Move off-stage and stop. #CSprite.CONTINUE = 4 # Move on forever. # Action for the sprite when it reaches a border. #self.mBoundAction = CSprite.WRAP #create a default text image as a placeholder #This will usually be changed by a setImage call #self.font = pygame.font.Font("freesansbold.ttf", 30) self.font = pygame.font.Font('assets/fonts/DejaVuSans.ttf', 30) self.mImageMaster = self.font.render(">sprite>", True, (0, 0,0), (0xFF, 0xFF, 0xFF)) # Set the image of the sprite. self.image = self.mImageMaster self.rect = self.image.get_rect() # Set the position of the image. # oldCenter is used to draw the trace in drawTrace(). # oldCenter is the position of the sprite in the previous frame. #self.oldCenter = (self.mPos.getX(), self.mPos.getY()) #self.rect.center = (self.mPos.getX(), self.mPos.getY()) self.calculatePositionWithOffset() # Angle of rotation in degrees used for movement calculations. self.mAngle = 0 # Angle of rotation in degrees of the sprite image (usually the same as mAngle). self.mRotation = 0 #self.pressed = False self.mactive = False self.mclicked = False self.states = {} self.currentState = "default" self.mTimeState = 0 def setRegistrationPointOffset(self, aOffsetX, aOffsetY): self.mOffsetX = aOffsetX self.mOffsetY = aOffsetY def setXY(self, aX, aY): #print 'alan', dir(self.mPos) self.mPos.setXY(aX, aY) #self.rect.center = (self.mPos.getX(), self.mPos.getY()) self.calculatePositionWithOffset() def setVelXY(self, aVelX, aVelY): self.mVel.setXY(aVelX, aVelY) self.mVel.truncate(self.maxSpeed) def setVelVec(self, aVec): self.mVel.setXY(aVec.getX(), aVec.getY()) self.mVel.truncate(self.maxSpeed) def setAccelXY(self, aAccelX, aAccelY): self.mAccel.setXY(aAccelX, aAccelY) def setMaxSpeed(self, aMaxSpeed): self.maxSpeed = aMaxSpeed def update(self): self.mTimeState += 1 #self.oldCenter = self.rect.center # Run the logic for the sprite. self.updateLogic() # Calculate next position based on angle and velocity. self.__updatePosition() # Rotate the image according to the angle of the sprite. #self.__rotateSpriteImage() #self.checkBounds() #self.rect.center = (self.mPos.getX(), self.mPos.getY()) # TODO: Test this. Need to pass a reference to background to draw. # self.drawTrace((255,0,0)) self.mVel.add(self.mAccel) self.mVel.truncate(self.maxSpeed) #if (self.mVel.getX() > self.maxSpeed): # self.mVel.setX(self.maxSpeed) #if (self.mVel.getY() > self.maxSpeed): # self.mVel.setY(self.maxSpeed)""" self.mPos.add(self.mVel) self.calculatePositionWithOffset() self.mclicked = False if CMouse().firstPress(): if self.rect.collidepoint(CMouse().getPos()): #print("first press in button") self.mactive = True #check for mouse release if self.mactive == True: if CMouse().release(): self.mactive = False if self.rect.collidepoint(CMouse().getPos()): #print("release in button") self.mclicked = True #--------------------------------------------------------------------------- # updateLogic(). # Override this method to implement the sprite's response to events. # This is the function that contains the logic of the sprite (it's behaviour). # # Parameters: # Nothing. # # Returns: Nothing. #--------------------------------------------------------------------------- def updateLogic(self): pass #--------------------------------------------------------------------------- # __updatePosition(). # Calculate the next position of the sprite based on angle and velocity. # # Parameters: # Nothing. # # Returns: Nothing. #--------------------------------------------------------------------------- def __updatePosition(self): theta = CMath.degToRad(self.mAngle) self.dx = math.cos(theta) * self.mSpeed self.dy = math.sin(theta) * self.mSpeed self.dy *= -1 self.mPos.setX(self.mPos.getX() + self.dx) self.mPos.setY(self.mPos.getY() + self.dy) #--------------------------------------------------------------------------- # __rotateSpriteImage(). # Rotates the sprite image according to the rotation attribute. # The registration point is assumed to be in the center of the image. # This function is called in update() every frame. # # Parameters: # Nothing. # # Returns: Nothing. #--------------------------------------------------------------------------- def __rotateSpriteImage(self): oldCenter = self.rect.center self.oldCenter = oldCenter self.image = pygame.transform.rotate(self.mImageMaster, self.mRotation) self.rect = self.image.get_rect() self.rect.center = oldCenter # Debug: Draw the collision circle. #pygame.draw.circle(self.image, (255,0,0), (self.rect.w/2 , self.rect.h/2), self.radius, 2) def setSpeed(self, speed): """ immediately sets the objects speed to the given value. """ self.mSpeed = speed def speedUp(self, amount): """ changes speed by the given amount Use a negative value to slow down """ self.mSpeed += amount if self.mSpeed < self.minSpeed: self.mSpeed = self.minSpeed if self.mSpeed > self.maxSpeed: self.mSpeed = self.maxSpeed #TODO def setAngle(self, direction): """ sets both the direction of motion and visual rotation to the given angle If you want to set one or the other, set them directly. Angle measured in degrees """ self.mAngle = direction self.mRotation = direction #--------------------------------------------------------------------------- # turnBy(). # Turn by the sprite by given number of degrees. Changes both angles (the # angle of the sprite and the rotation attribute of the image). A positive # angle is counter-clockwise and a negative angle is clockwise. # # Parameters: # aAngleInc: Number of degrees to add to the current rotation angle. # # Returns: Nothing. #--------------------------------------------------------------------------- def turnBy(self, aAngleInc): self.mAngle = CMath.toStandardAngle(self.mAngle + aAngleInc) self.mRotation = self.mAngle # TODO: Es igual que la anterior pero solo para mRotation # Estas cuentas estan mal, hay que usar toStandardAngle(). def rotateBy(self, amt): """ change visual orientation by given number of degrees. Does not change direction of travel. """ self.mRotation += amt if self.mRotation > 360: self.mRotation = amt if self.mRotation < 0: self.mRotation = 360 - amt #---------------------------------------------------------------------------------------------------------- # setImage(). # Loads the given file name as the master image that then is rotated. # The sprite must be facing east (angle 0). In the main loop the sprite is rotated automatically. # # Parameters: # aImageFilename: File path of the sprite image to be loaded. # aIsTransparent: If the image is transparent (True) or not (True, by default). # # Returns: Nothing. #---------------------------------------------------------------------------------------------------------- def loadImage(self, aImageFilename, aIsTransparent=True): self.mImageMaster = Image.loadImage(aImageFilename, aIsTransparent) self.setImage(self.mImageMaster) #self.image = self.mImageMaster #self.rect = self.image.get_rect() #self.rect.center = (self.mPos.getX(), self.mPos.getY()) def setImage(self, aImage): self.mImageMaster = aImage self.image = aImage self.rect = self.image.get_rect() #self.rect.center = (self.mPos.getX(), self.mPos.getY()) self.calculatePositionWithOffset() def setDX(self, dx): """ changes dx value and updates vector """ self.dx = dx self.updateVector() def addDX(self, amt): """ adds amt to dx, updates vector """ self.dx += amt self.updateVector() def setDY(self, dy): """ changes dy value and updates vector """ self.dy = dy self.updateVector() def addDY(self, amt): """ adds amt to dy and updates vector """ self.dy += amt self.updateVector() def setComponents(self, components): """ expects (dx, dy) for components change speed and angle according to dx, dy values """ (self.dx, self.dy) = components self.updateVector() def setBoundAction (self, aBoundAction): self.mBoundAction = aBoundAction def getBoundAction(self): return self.mBoundAction def setPosition (self, position): """ place the sprite directly at the given position expects an (x, y) tuple """ self.mPos.setX(position.getX()) self.mPos.setY(position.getY()) def moveBy (self, vector): """ move the sprite by the (dx, dy) values in vector automatically calls checkBounds. Doesn't change speed or angle settings. """ (dx, dy) = vector self.mPos.x += dx self.mPos.y += dy self.checkBounds() def forward(self, amt): """ move amt pixels in the current direction of travel """ #calculate dx dy based on current direction radians = self.mAngle * math.pi / 180 dx = amt * math.cos(radians) dy = amt * math.sin(radians) * -1 self.mPos.x += dx self.mPos.y += dy def addForce(self, amt, angle): """ apply amt of thrust in angle. change speed and dir accordingly add a force straight down to simulate gravity in rotation direction to simulate spacecraft thrust in dir direction to accelerate forward at an angle for retro-rockets, etc. """ #calculate dx dy based on angle radians = angle * math.pi / 180 dx = amt * math.cos(radians) dy = amt * math.sin(radians) * -1 self.dx += dx self.dy += dy self.updateVector() def updateVector(self): #calculate new speed and angle based on dx, dy #call this any time you change dx or dy self.mSpeed = math.sqrt((self.dx * self.dx) + (self.dy * self.dy)) dy = self.dy * -1 dx = self.dx radians = math.atan2(dy, dx) self.mAngle = radians / math.pi * 180 def setSpeedLimits(self, amax, amin): """ determines maximum and minimum speeds you will allow through speedUp() method. You can still directly set any speed you want with setSpeed() Default values: max: 10 min: -3 """ self.maxSpeed = amax self.minSpeed = amin def dataTrace(self): """ utility method for debugging print major properties extend to add your own properties """ print "x: %d, y: %d, speed: %.2f, dir: %.f, dx: %.2f, dy: %.2f" % \ (self.mPos.x, self.mPos.y, self.mSpeed, self.mAngle, self.dx, self.dy) def mouseDown(self): """ boolean function. Returns True if the mouse is clicked over the sprite, False otherwise """ self.pressed = False if CMouse().pressed(): if self.rect.collidepoint(CMouse().getPos()): self.pressed = True return self.pressed def mouseOver(self): if not CMouse().pressed(): if self.rect.collidepoint(CMouse().getPos()): return True else: return False else: return False def clicked(self): """ Boolean function. Returns True only if mouse is pressed and released over sprite """ """released = False if self.pressed: if (not CMouse().pressed()): if self.rect.collidepoint(CMouse().getPos()): released = True return released""" return self.mclicked def collidesWith(self, target): """ boolean function. Returns True if the sprite is currently colliding with the target sprite, False otherwise """ collision = False if self.rect.colliderect(target.rect): collision = True return collision def collidesGroup(self, target): """ wrapper for pygame.sprite.collideany simplifies checking sprite - group collisions returns result of collision check (sprite from group that was hit or None) """ return pygame.sprite.spritecollideany(self, target) def distanceTo(self, point): """ returns distance to any point in pixels can be used in circular collision detection """ (pointx, pointy) = point dx = self.mPos.x - pointx dy = self.mPos.y - pointy dist = math.sqrt((dx * dx) + (dy * dy)) return dist def dirTo(self, point): """ returns direction (in degrees) to a point """ (pointx, pointy) = point dx = self.mPos.x - pointx dy = self.mPos.y - pointy dy *= -1 radians = math.atan2(dy, dx) direction = radians * 180 / math.pi direction += 180 return direction #TODO: no hay scene, ver como agarra el background... def drawTrace(self, color=(0x00, 0x00, 0x00)): """ traces a line between previous position and current position of object """ pygame.draw.line(self.scene.background, color, self.oldCenter, self.rect.center, 3) self.screen.blit(self.scene.background, (0, 0)) def addState(self, stateName, stateImageFile): """ Creates a new sprite state with the associated name and image. Useful to build multi-state sprites. """ #load the image # TODO: Use the loading function. tempImage = pygame.image.load(stateImageFile) tempImage.convert() self.states[stateName] = tempImage def setState(self, stateName): """ attempts to set the sprite to the indicated state (image) """ self.mImageMaster = self.states[stateName] self.rect = self.mImageMaster.get_rect() self.currentState = stateName def getState(self): """ returns the current state name (default if no states have been explicitly set) """ return self.currentState #--------------------------------------------------------------------------- # setBounds(). # Sets the boundaries used in checkBounds() to implement sprite behaviour # when it reaches the edge of the screen or this bound rectangle. # # Parameters: # aMinX: Horizontal coordinate of the left edge. # aMaxX: Horizontal coordinate of the right edge. # aMinY: Vertical coordinate of the top edge. # aMaxY: Vertical coordinate of the bottom edge. # # Returns: Nothing. #--------------------------------------------------------------------------- def setBounds(self, aMinX, aMinY, aMaxX, aMaxY): self.mMinX = aMinX self.mMaxX = aMaxX self.mMinY = aMinY self.mMaxY = aMaxY def checkBounds(self): """ checks boundary and acts based on self.BoundAction. WRAP: wrap around screen (default) BOUNCE: bounce off screen STOP: stop at edge of screen HIDE: move off stage and wait CONTINUE: keep going at present course and speed automatically called by update """ # TODO scrWidth = self.mMaxX scrHeight = self.mMaxY #print scrWidth, scrHeight #create variables to simplify checking offRight = offLeft = offTop = offBottom = offScreen = False if self.mPos.x > scrWidth: offRight = True if self.mPos.x < 0: offLeft = True if self.mPos.y > scrHeight: offBottom = True if self.mPos.y < 0: offTop = True if offRight or offLeft or offTop or offBottom: offScreen = True if self.mBoundAction == self.WRAP: if offRight: self.mPos.x = 0 if offLeft: self.mPos.x = scrWidth if offBottom: self.mPos.y = 0 if offTop: self.mPos.y = scrHeight elif self.mBoundAction == self.BOUNCE: if offLeft or offRight: self.dx *= -1 if offTop or offBottom: self.dy *= -1 self.updateVector() self.mRotation = self.mAngle elif self.mBoundAction == self.STOP: if offScreen: self.mSpeed = 0 elif self.mBoundAction == self.HIDE: if offScreen: self.mSpeed = 0 self.setPosition((-1000, -1000)) elif self.mBoundAction == self.CONTINUE: pass else: # assume it's continue - keep going forever pass def destroy(self): self.font = None self.mImageMaster = None self.image = None def calculatePositionWithOffset(self): self.rect = self.image.get_rect() #print self.mOffsetX, self.mOffsetY self.rect.x = self.mPos.getX() - self.mOffsetX self.rect.y = self.mPos.getY() - self.mOffsetY def getX(self): return self.mPos.getX() def getY(self): return self.mPos.getY() def setTimeState(self, aTimeState): self.mTimeState = aTimeState def getTimeState(self): return self.mTimeState def getSize(self): if self.image: rect = self.image.get_rect() return (rect[2], rect[3]) else: return (0,0)