def __init__(self, imageDims, fieldOfView, clippingPlanes, originVector, terrainDims, terrainScale, terrainOffsetVector, terrainSpread): # Set instance variables self.imageDims = imageDims self.fieldOfView = fieldOfView self.clippingPlanes = clippingPlanes # Create the world axes set to define the coordinate system xAxis = Vector(1, 0, 0) yAxis = Vector(0, 1, 0) zAxis = Vector(0, 0, 1) self.axes = [originVector, xAxis, yAxis, zAxis] # Create the camera self.camera = Camera(self.fieldOfView,\ self.clippingPlanes,\ self.imageDims,\ self.axes) # Terrain generation self.terrainDims = terrainDims self.terrainScale = terrainScale self.terrainOffsetVector = terrainOffsetVector self.terrainSpread = terrainSpread self.terrainGenerator = TerrainGenerator(self.terrainDims, self.terrainScale, self.terrainSpread) self.terrainCache = self.generateTerrainRasterPoints( self.terrainOffsetVector, -1.0, 0.0, 0.0)
def generateEntity(self, positionVector, hitboxRadius): # Generate the entity-to-world transformation matrix entityAxes = [ positionVector, self.world.axes[1], self.world.axes[2], self.world.axes[3] ] entityMatrix = Entity.generateEntityToWorldMatrix(entityAxes) # Define the hitbox vectors in $Object$ coordinates hitboxVectors = [Vector(-hitboxRadius, -hitboxRadius, -hitboxRadius),\ Vector(+hitboxRadius, +hitboxRadius, +hitboxRadius)] return Entity(entityMatrix, hitboxVectors)
def launchProjectileFromEnemy(self, enemyEntity, hitboxRadius, velocityMagnitude, damage): # Get the velocity vector required to hit the player from the entity's # position. velocityVector = Vector.getVelocityFromPointToPoint( enemyEntity.getPosition(), self.playerPosition, velocityMagnitude) self.launchProjectile(enemyEntity.getPosition(), velocityVector, hitboxRadius, damage, True)
def spawnEnemy(self, positionVector, hitboxRadius=5, health=100, projectileStrength=10): # Generate the entity-to-world transformation matrix enemyAxes = [ positionVector, self.world.axes[1], self.world.axes[2], self.world.axes[3] ] enemyMatrix = Entity.generateEntityToWorldMatrix(enemyAxes) # Define the hitbox vectors in $Object$ coordinates hitboxVectors = [Vector(-hitboxRadius, -hitboxRadius, -hitboxRadius),\ Vector(+hitboxRadius, +hitboxRadius, +hitboxRadius)] self.entities.append( Enemy(enemyMatrix, hitboxVectors, health, projectileStrength))
def __init__(self, entityToWorldMatrix, hitboxVectorList, health, projectileStrength, velocityVector=Vector(0, 0, 0)): super().__init__(entityToWorldMatrix, hitboxVectorList, velocityVector) self.health = health self.projectileStrength = projectileStrength
def launchProjectile(self, positionVector, velocityVector, hitboxRadius, damage, launchedByEnemy=False): # Generate the projectile-to-world transformation matrix projectileAxes = [ positionVector, self.world.axes[1], self.world.axes[2], self.world.axes[3] ] projectileMatrix = Entity.generateEntityToWorldMatrix(projectileAxes) # Define the hitbox vectors in $Object$ coordinates hitboxVectors = [Vector(-hitboxRadius, -hitboxRadius, -hitboxRadius),\ Vector(+hitboxRadius, +hitboxRadius, +hitboxRadius)] self.entities.append( Projectile(projectileMatrix, hitboxVectors, velocityVector, damage, launchedByEnemy))
def moveCamera(self, dx, dy, dz): origin = self.axes[0] self.axes[0] = Vector(origin.x + dx, origin.y + dy, origin.z + dz) self.camera = Camera(self.fieldOfView,\ self.clippingPlanes,\ self.imageDims,\ self.axes) self.terrainOffsetVector.x += dx self.terrainOffsetVector.y += dy self.terrainOffsetVector.z += dz self.setTerrainCache(self.terrainOffsetVector)
def generateTerrainVectors(self, terrainOffsetVector, zSign, xShift, zShift): perlinGrid = self.generateGrid(xShift, zShift) yank = len(perlinGrid) yeet = range(len(perlinGrid[0])) offsetX = terrainOffsetVector.x offsetY = terrainOffsetVector.y offsetZ = terrainOffsetVector.z return [[ Vector(self.spread * (x - yank // 2) + offsetX, perlinGrid[x][z] + offsetY, (zSign * (self.spread * z + offsetZ))) for z in yeet ] for x in range(len(perlinGrid))]
def __init__(self, entityToWorldMatrix, hitboxVectorList, velocityVector = Vector(0,0,0)): if (len(hitboxVectorList) != 2): raise Exception("Invalid hitbox vector list length") self.entityToWorldMatrix = entityToWorldMatrix self.worldToEntityMatrix = Matrix.inverse( self.entityToWorldMatrix ) self.hitboxVectorList = hitboxVectorList self.velocityVector = velocityVector self.cachedWorldHitboxVectors = [] self.cachedEntityHitboxVertexVectors = [] self.cachedWorldHitboxVertexVectors = [] self.regenWorldHitboxVectors = True self.regenEntityHitboxVertexVectors = True self.regenWorldHitboxVertexVectors = True self.visible = True
def pointVectorMultiply(self, vector): outputX = vector.x * self.values[0][0] +\ vector.y * self.values[1][0] +\ vector.z * self.values[2][0] +\ vector.w * self.values[3][0] outputY = vector.x * self.values[0][1] +\ vector.y * self.values[1][1] +\ vector.z * self.values[2][1] +\ vector.w * self.values[3][1] outputZ = vector.x * self.values[0][2] +\ vector.y * self.values[1][2] +\ vector.z * self.values[2][2] +\ vector.w * self.values[3][2] outputW = vector.x * self.values[0][3] +\ vector.y * self.values[1][3] +\ vector.z * self.values[2][3] +\ vector.w * self.values[3][3] return Vector(outputX / outputW, outputY / outputW, outputZ / outputW, 1)
def getEntityHitboxVertexVectors(self): if (not self.regenEntityHitboxVertexVectors): return self.cachedEntityHitboxVertexVectors minimumVector = min(self.hitboxVectorList) maximumVector = max(self.hitboxVectorList) hitboxVertexVectors = [] (x0, y0, z0) = (minimumVector.x, minimumVector.y, minimumVector.z) (x1, y1, z1) = (maximumVector.x, maximumVector.y, maximumVector.z) for x in (x0, x1): for y in (y0, y1): for z in (z0, z1): hitboxVertexVectors.append( Vector(x, y, z) ) self.cachedEntityHitboxVertexVectors = hitboxVertexVectors self.regenEntityHitboxVertexVectors = False return hitboxVertexVectors
def getPosition(self): x = self.entityToWorldMatrix.values[3][0] y = self.entityToWorldMatrix.values[3][1] z = self.entityToWorldMatrix.values[3][2] return Vector(x,y,z)
def gameLoop(self): timeStartGameLoop = time.time() if "w" in self.keysPressed: self.zStep = +self.movementSpeed elif "s" in self.keysPressed: self.zStep = -self.movementSpeed else: self.zStep = 0 if "a" in self.keysPressed: self.xStep = +self.movementSpeed elif "d" in self.keysPressed: self.xStep = -self.movementSpeed else: self.xStep = 0 if not self.paused and not self.gameIsOver: (nearClip, farClip) = self.clippingPlanes # Move terrain if (self.xStep != 0 or self.zStep != 0): self.shiftTerrain(self.xStep, self.zStep) self.updateTerrain = True # Launch projectile if (self.queueProjectileLaunch and self.playerShootTimer >= self.playerShootTimeout and self.ammoCount > 0): self.launchProjectile(self.playerPosition, Vector(0, 0, -self.projectileSpeed), self.projectileHitboxRadius, self.playerProjectileDamage) self.ammoCount -= 1 self.playerShootTimer = 0 elif self.queueProjectileLaunch: self.queueProjectileLaunch = False # Spawn another enemy if the timer is done if (self.enemySpawnTimer > (len(self.entities)**0.4) * self.enemySpawnDelay and len(self.entities) <= self.maxEntityCount): self.spawnEnemy( Vector(random.randint(-30, 30), 0, random.randint(-80, -35))) self.enemySpawnTimer = 0 # Ammo regen if (self.ammoRegenTimer > self.ammoRegenDelay): self.ammoCount += self.ammoRegenAmount self.ammoRegenTimer = 0 self.ammoCount = min(self.initAmmoCount, self.ammoCount) # Shield regen if (self.shieldRegenTimer > self.shieldRegenDelay and self.shieldPoints > 0): self.shieldPoints += self.shieldRegenAmount self.shieldRegenTimer = 0 self.shieldPoints = min(self.initShieldPoints, self.shieldPoints) for entity in self.entities: entityVelocity = entity.velocityVector entity.translate(entityVelocity.x - self.xStep, entityVelocity.y, entityVelocity.z + self.zStep) if (abs(entity.entityToWorldMatrix.values[3][2]) > 2 * farClip): # Despawn condition self.entities.remove(entity) elif (abs(entity.entityToWorldMatrix.values[3][2] > farClip) ): # Not visible condition entity.visible = False elif not entity.visible: entity.visible = True # Shoot on time if an enemy. If projectile, check collisions if isinstance(entity, Enemy): if self.enemyShootTimer > self.enemyShootDelay: self.launchProjectileFromEnemy( entity, self.projectileHitboxRadius, self.projectileSpeed, self.enemyProjectileDamage) self.enemyShootTimer = 0 else: for otherEntity in self.entities: if (entity != otherEntity and not isinstance(otherEntity, Projectile)): if entity.spawnedByEnemy: if entity.collidesWith( self.playerEntity ) or self.playerEntity.collidesWith(entity): self.playerHit(entity.strength) if entity in self.entities: self.entities.remove(entity) else: if entity.collidesWith( otherEntity ) or otherEntity.collidesWith(entity): if (otherEntity in self.entities): self.entities.remove(otherEntity) if (entity in self.entities): self.entities.remove(entity) self.score += self.scoreIncrement # Draw with new information self.drawAll(drawTerrain=self.updateTerrain, drawHitboxes=not self.paused and not self.gameIsOver, drawOverlay=True) self.updateTerrain = False timeEndGameLoop = time.time() timeElapsedGameLoop = timeEndGameLoop - timeStartGameLoop # This is necessary just in case the game lags. If the game loop takes # longer than the timer delay, then python will pin the CPU usage at # 100 unless a small buffer is added. if (timeElapsedGameLoop >= self.timerDelay): self.updateTimers(timeElapsedGameLoop + self.frameBufferTime) self.after(timeElapsedGameLoop + self.frameBufferTime, self.gameLoop) else: self.updateTimers(self.timerDelay) self.after(self.timerDelay, self.gameLoop)
def __init__(self, width=1080, height=720): # Tkinter initialization super().__init__() if winsoundAvailable: winsound.PlaySound( "tatchwave.wav", winsound.SND_LOOP + winsound.SND_ASYNC | winsound.SND_ALIAS) ### User customization # User View Variables self.fieldOfView = 90 self.clippingPlanes = (0.1, 100) self.originVector = Vector(0, -14, 0) self.terrainDims = (54, 29) self.terrainScale = 6.4 self.terrainOffsetVector = Vector(0, 0, 12) self.terrainSpread = 1.8 # User Gameplay Variables self.targetFPS = 30 self.movementSpeed = 1.0 self.backgroundColor = "black" self.terrainColor = "white" self.enemyColor = "red" self.projectileColor = "green" self.playerShootTimeout = 50 self.enemySpawnDelay = 450 self.enemyShootDelay = 400 self.maxEntityCount = 10 self.ammoRegenDelay = 450 self.shieldRegenDelay = 1250 self.ammoRegenAmount = 1 self.shieldRegenAmount = 5 self.initAmmoCount = 5 self.initHealthPoints = 100 self.initShieldPoints = 60 self.playerHitboxRadius = 5 self.projectileHitboxRadius = 0.75 self.projectileSpeed = 4 self.playerProjectileDamage = 20 self.enemyProjectileDamage = 14 self.scoreIncrement = 100 ### Game variables # Set the instance variables self.width = width self.height = height # Game state self.paused = False self.gameIsOver = False self.keysPressed = set() self.score = 0 self.ammoCount = self.initAmmoCount self.healthPoints = self.initHealthPoints self.shieldPoints = self.initShieldPoints self.updateTerrain = True self.queueProjectileLaunch = False # Timing self.frameBufferTime = 25 self.timerDelay = 1000 // self.targetFPS self.enemySpawnTimer = self.enemySpawnDelay # Spawn instantly self.enemyShootTimer = 0 self.playerShootTimer = 0 self.ammoRegenTimer = 0 self.shieldRegenTimer = 0 # Drawing, terrain movement, entities self.terrainLineIdsList = [] self.hitboxLineIdsList = [] self.zStep, self.xStep = 0.0, 0.0 self.xShift, self.zShift = 0.0, 0.0 self.entities = [] # Overlay specifications and state self.overlayMargin = 10 self.overlayPauseSize = min(self.width // 20, self.height // 20) self.overlayPauseSizeSelectionModifier = 1.4 self.overlayPauseSelected = False ### Operations # Create the frame, set window pos, set background color, set resizeable self.tatchFrame = TatchFrame(self, self.width, self.height) self.geometry(str(self.width) + "x" + str(self.height) + "+0+0") self.tatchFrame.tatchCanvas.config(background=self.backgroundColor) self.resizable(False, False) # Create the necessary game objects self.world = World((self.width, self.height), self.fieldOfView, self.clippingPlanes, self.originVector, self.terrainDims, self.terrainScale, self.terrainOffsetVector, self.terrainSpread) self.overlay = Overlay(self.width, self.height, self.tatchFrame.tatchCanvas, self.overlayMargin, self.overlayPauseSize, self.overlayPauseSizeSelectionModifier, self.initHealthPoints, self.initShieldPoints, self.initAmmoCount) # Bind keyPress/mouse events to their respective functions self.bind("<KeyPress>", self.keyDown) self.bind("<KeyRelease>", self.keyUp) self.bind("<Motion>", self.mouseMoved) self.bind("<Button>", self.mousePressed) # Bind window events self.protocol("WM_DELETE_WINDOW", self.closeWindow) # Create the player entity and spawn the first entity self.playerPosition = Vector(self.originVector.x, self.originVector.y / 4, self.originVector.z) self.playerEntity = self.generateEntity(self.playerPosition, self.playerHitboxRadius)