• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.

dr01d3k4

Members
  • Content count

    22
  • Joined

  • Last visited

Community Reputation

491 Neutral

About dr01d3k4

  • Rank
    Member

Personal Information

  • Location
    England
  1. You could look into a preexisting solution such as Google Play Game Services. I haven't used it for multiplayer before though so I don't know how thin the clients would be.
  2. I'm not entirely sure what you're doing. Your code is hard to read and follow, and has lots of duplicated code. I made this example that shows moving the player 1 tile at a time (the collision for this is a lot easier). import pygame; from random import randint; BLACK = (  0,   0,   0); WHITE = (255, 255, 255); GREEN = (  0, 255,   0); RED   = (255,   0,   0); BACKGROUND_COLOUR = BLACK; TILE_COLOUR = GREEN; PLAYER_COLOUR = WHITE; TILE_NONE = False; TILE_SOLID = True; SCREEN_WIDTH = 700; SCREEN_HEIGHT = 500; SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT); MAP_WIDTH = 64; MAP_HEIGHT = 32; TILE_SIZE = 16; TITLE = "Maze Game"; FPS = 60; def sign(x):     return (x < 0) and -1 or (x > 0) and 1 or 0; class Player(object):     def __init__(self, x, y):         self.x = x;         self.y = y;     def move(self, dx, dy, tiles):         if (dx != 0):             if (dy != 0):                 return;             newX = self.x + sign(dx);             if (not isSolidTile(tiles, newX, self.y)):                 self.x = newX;         elif (dy != 0):             if (dx != 0):                 return;             newY = self.y + sign(dy);             if (not isSolidTile(tiles, self.x, newY)):                 self.y = newY; def generateTiles(width, height):     tiles = [ ];          for x in range(0, width):         tiles.append([ ]);         for y in range(0, height):             tileType = TILE_NONE;             if ((x == 0) or (y == 0) or (x == width - 1) or (y == height - 1) or (randint(0, 10) < 2)):                 tileType = TILE_SOLID;             tiles[x].append(tileType);     return tiles; def isSolidTile(tiles, x, y):     return ((x < 0) or (y < 0) or (x >= MAP_WIDTH) or (y >= MAP_HEIGHT) or (tiles[x][y] == TILE_SOLID)); def renderTiles(screen, tiles):     for x in range(0, len(tiles)):         for y in range(0, len(tiles[x])):             if (isSolidTile(tiles, x, y)):                 pygame.draw.rect(screen, TILE_COLOUR, [x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE], 0); def renderPlayer(screen, player):     pygame.draw.rect(screen, PLAYER_COLOUR, [player.x * TILE_SIZE, player.y * TILE_SIZE, TILE_SIZE, TILE_SIZE], 0); def playGame():     pygame.init();     screen = pygame.display.set_mode(SCREEN_SIZE);     pygame.display.set_caption(TITLE);     clock = pygame.time.Clock();     playing = True;     tiles = generateTiles(MAP_WIDTH, MAP_HEIGHT);     player = Player(2, 2);     while (playing):         for event in pygame.event.get():             if (event.type == pygame.QUIT):                 playing = False;             if (event.type == pygame.KEYDOWN):                 if (event.key == pygame.K_LEFT):                     player.move(-1, 0, tiles);                 if (event.key == pygame.K_RIGHT):                     player.move(1, 0, tiles);                 if (event.key == pygame.K_UP):                     player.move(0, -1, tiles);                 if (event.key == pygame.K_DOWN):                     player.move(0, 1, tiles);         screen.fill(BACKGROUND_COLOUR);         renderTiles(screen, tiles);         renderPlayer(screen, player);         pygame.display.flip();         clock.tick(FPS);     pygame.quit(); if (__name__ == "__main__"):     playGame(); The way it works is: There is a 2 dimensional array of booleans - true for tile being there; false if there isn't When rendering, iterate through these and if there's a tile, draw it at that location * TILE_SIZE (in a real game you may want to account for camera moving and only thinking about what is actually on screen) Player's position is (x, y), which is the location of the top left corner in tile space. The size of the player is assumed to be 1x1. Just like with tiles, when rendering need to scale by TILE_SIZE When the player attempts to move, +- 1 in the direction they want to move, if there isn't a tile there then it's safe to move, else do nothing
  3. If the walls are all square, then you can just use a tile grid. grid = [ [True, True, True, True], [True, False, True, True], [True, False, False, True], [True, True, True, True] ]; In a way, this is similar to storing the actual pixels, but using 16/32/64...(whatever your tile size is) times less space. This does mean that when rendering you need to make sure to multiply by tile size: TILE_SIZE = 16; for y in range(grid): for x in range(grid[y]): # isSolid for here could just return the cell because the true/false represents its solidity # But if you want to change what data you store about each cell, then it's good to already have this in its own function if (isSolid(grid[y][x])): # I don't know Pygame's API                         drawRectangle(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE, CELL_COLOUR); For the physics, you need to step x and y individually. A good guide for this is in "the guide to implementing 2d platformers" (although it says platformers, it still works in top down, and it's a good read too if you want to make a platformer next).
  4. I think the problem is because when you try to use a bitwise operator on two enum values, it returns an int. I searched on google and found this stackoverflow post about using bitwise operators with enums. eidolon's solution was to override the operator for your enum, so it'd look like this: inline StructureFunctions operator|(StructureFunctions a, StructureFunctions b) { return static_cast<StructureFunctions>(static_cast<int>(a) | static_cast<int>(b)); } inline StructureFunctions operator&(StructureFunctions a, StructureFunctions b) { return static_cast<StructureFunctions>(static_cast<int>(a) & static_cast<int>(b)); } Edit: I just realized that using these operators could return a number not defined by a member of the enum. I don't know how enums in C++ work though so I don't know if this is a problem or not. There were some answers on the stack overflow question saying to use int, rather than StructureFunctions as the type of your variables using it.
  5. In my JavaScript + HTML5 canvas games, I've written and used this file to deal with mouse input: (function (window) { var mouse = { down: false, x: -1, y: -1 }; var previousMouse = { down: false, x: -1, y: -1 }; var mouseLetGo = false; var updateMouse = function () { previousMouse.down = mouse.down; previousMouse.x = mouse.x; previousMouse.y = mouse.y; if (mouseLetGo) { mouse.down = false; mouseLetGo = false; } }; var getMousePosition = function (event) { var x = -1; var y = -1; if ((event.x === undefined) || (event.y === undefined)) { x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; } else { x = event.x; y = event.y; } x -= canvas.offsetLeft + canvasBorderThickness; y -= canvas.offsetTop + canvasBorderThickness; return [x, y]; }; var setMousePosition = function (event) { event = event || window.event; var pos = getMousePosition(event); mouse.x = pos[0]; mouse.y = pos[1]; }; var onMouseDown = function (event) { mouse.down = true; setMousePosition(event); }; var onMouseMove = function (event) { setMousePosition(event); }; var onMouseUp = function (event) { mouseLetGo = true; setMousePosition(event); }; var isMouseDown = function () { return mouse.down; }; var isMouseUp = function () { return !mouse.down }; var getMouseX = function () { return mouse.x; }; var getMouseY = function () { return mouse.y; }; var isMouseOver = function (x, y, width, height) { return ((getMouseX() >= x) && (getMouseX() <= x + width) && (getMouseY() >= y) && (getMouseY() <= y + height)); }; var wasMouseDown = function () { return previousMouse.down; }; var wasMouseUp = function () { return !previousMouse.down; }; var setMouseDown = function (down) { mouse.down = down; }; window.updateMouse = updateMouse; window.onMouseDown = onMouseDown; window.onMouseMove = onMouseMove; window.onMouseUp = onMouseUp; window.isMouseDown = isMouseDown; window.isMouseUp = isMouseUp; window.getMouseX = getMouseX; window.getMouseY = getMouseY; window.isMouseOver = isMouseOver; window.wasMouseDown = wasMouseDown; window.wasMouseUp = wasMouseUp; window.setMouseDown = setMouseDown; })(window); In onload, I connect those functions up to the events: canvas.onmousedown = onMouseDown; canvas.onmousemove = onMouseMove; canvas.onmouseup = onMouseUp; In each frame of the game, I call the updateMouse(); function.   The getMouseX/Y() functions return the pixel position of the mouse based on the top left corner of the canvas. This means that if x < 0 or x > canvasWidth (and same for y), then it is out of the screen. The mouse events don't fire once the mouse is outside the canvas though (apart from the first with a -1/canvasWidth + 1/canvasHeight + 1 value).       Using your method, you could try to clamp the last known position of the mouse to the screen. // Assume mouse closest to left side first var drawAtX = 0; var drawAtY = mouse.y; var shortestDistance = mouse.x; // If closer to top if (mouse.y < shortestDistance) { shortestDistance = mouse.y; drawAtX = mouse.x; drawAtY = 0; } // Right side if (canvasWidth - mouse.x < shortestDistance) { shortestDistance = canvasWidth - mouse.x; drawAtX = canvasWidth; drawAtY = mouse.y; } // Bottom side if (canvasHeight - mouse.y < shortestDistance) { shortestDistance = canvasHeight - mouse.y; drawAtX = mouse.x; drawAtY = canvasHeight; } Or if you know the 2 last positions of the mouse (and so deltaX and deltaY), you could continue in the direction the mouse was moving until it hits the edge.
  6.   I too recommend Love2d. For physics, you can write your own or it provides you with bindings to Box2d (although the tutorials on the wiki are a bit lacking I find), and for graphics you can write shaders in a slightly modified GLSL (afaik the syntax is the same but some names changed). I haven't found many tutorials, but there is good documentation on their wiki and example code (including a Tiled map loader). I also don't know about running it on mobile or the web, but it runs on windows, mac and linux and you can code in any editor. Love2d was also used in this awesome game, Mari0.   Lua's also a pretty good language. Quite fast and lightweight compared to other scripting languages like Python, but its standard library is quite small. I personally prefer to use Moonscript though (it compiles into Lua).
  7. I'm not entirely sure what you mean by "sub-expressions as they are compiled". Are you trying to rewrite the expression in infix/postfix notation or trying to evaluate an answer?   Also is this a homework question? They aren't allowed here and with the way you've worded it I'm not sure if this is an assignment or not.   After playing around for a bit, I got this which does both of those (though it's a bit ugly as it uses member variables to get around Java only allowing 1 return value). It's also not commented, but it should hopefully be clear what's going on: import java.util.ArrayList; import java.util.List; import java.util.Stack; public final class PrefixEvaluation { private final List<Token> tokens; private String infixExpression; private String postfixExpression; public PrefixEvaluation() { tokens = new ArrayList<Token>(); infixExpression = ""; postfixExpression = ""; } public void addToken(final Token token) { token.index = tokens.size(); tokens.add(token); } public String prefixExpressionToString() { final StringBuilder stringBuilder = new StringBuilder(); for (final Token token : tokens) { stringBuilder.append(token.value); stringBuilder.append(" "); } String expression = stringBuilder.toString(); expression = expression.trim(); return expression; } public double evaluate() throws Exception { final Stack<Token> operators = new Stack<Token>(); final Stack<Token> numbers = new Stack<Token>(); final Stack<String> infixExpressions = new Stack<String>(); final Stack<String> postfixExpressions = new Stack<String>(); try { while (tokens.size() > 0) { final Token currentToken = tokens.remove(0); if (currentToken.type == TokenType.NUMBER) { numbers.push(currentToken); } else if (currentToken.type == TokenType.OPERATOR) { operators.push(currentToken); } infixExpressions.push(currentToken.value); postfixExpressions.push(currentToken.value); while (operators.size() > 0) { final Token topOperator = operators.peek(); final int operatorIndex = topOperator.index; final int operandCount = getOperandCountForOperator(topOperator.value); if (numbers.size() >= operandCount) { Token number2 = numbers.peek(); if (number2.index > operatorIndex) { number2 = numbers.pop(); } else { break; } Token number1 = numbers.peek(); if (number1.index > operatorIndex) { number1 = numbers.pop(); } else { numbers.push(number2); break; } final Token operator = operators.pop(); String number1Expression = infixExpressions.pop(); String number2Expression = infixExpressions.pop(); String operatorExpression = infixExpressions.pop(); infixExpressions.push("(" + number2Expression + " " + operatorExpression + " " + number1Expression + ")"); number1Expression = postfixExpressions.pop(); number2Expression = postfixExpressions.pop(); operatorExpression = postfixExpressions.pop(); postfixExpressions.push(number2Expression + " " + number1Expression + " " + operatorExpression); final double result = evaluateResult(number1.value, operator.value, number2.value); final Token resultToken = new Token(Double.toString(result), TokenType.NUMBER, number2.index); numbers.push(resultToken); } else { break; } } } } catch (final IllegalArgumentException e) { e.printStackTrace(); } double answer = 0; if (numbers.size() == 1) { answer = Double.parseDouble(numbers.pop().value); } else { throw new Exception("Error in parsing"); } infixExpression = infixExpressions.pop(); postfixExpression = postfixExpressions.pop(); return answer; } private double evaluateResult(final String number1String, final String operator, final String number2String) { double result = 0; final double number1 = Double.parseDouble(number1String); final double number2 = Double.parseDouble(number2String); if (operator.equals("+")) { result = number1 + number2; } else if (operator.equals("-")) { result = number1 - number2; } else if (operator.equals("*")) { result = number1 * number2; } else if (operator.equals("/")) { result = number1 / number2; } else { throw new IllegalArgumentException("Operator unknown"); } return result; } private int getOperandCountForOperator(final String operator) { int operandCount = 0; if (operator.equals("+") || operator.equals("-") || operator.equals("*") || operator.equals("/")) { operandCount = 2; } else { throw new IllegalArgumentException("Operator unknown"); } return operandCount; } private static final class Token { public final String value; public final TokenType type; public int index; public Token(final String value, final TokenType type) { this(value, type, -1); } public Token(final String value, final TokenType type, final int index) { this.value = value; this.type = type; this.index = index; } @Override public String toString() { return value + " (" + type.toString() + ", " + index + ")"; } } private enum TokenType { OPERATOR, NUMBER } public static void main(final String[] args) throws Exception { final PrefixEvaluation prefixEvaluation = new PrefixEvaluation(); prefixEvaluation.addToken(new Token("*", TokenType.OPERATOR)); prefixEvaluation.addToken(new Token("+", TokenType.OPERATOR)); prefixEvaluation.addToken(new Token("12", TokenType.NUMBER)); prefixEvaluation.addToken(new Token("8", TokenType.NUMBER)); prefixEvaluation.addToken(new Token("+", TokenType.OPERATOR)); prefixEvaluation.addToken(new Token("3", TokenType.NUMBER)); prefixEvaluation.addToken(new Token("7", TokenType.NUMBER)); final String prefixExression = prefixEvaluation.prefixExpressionToString(); final double answer = prefixEvaluation.evaluate(); final String infixExpression = prefixEvaluation.infixExpression; final String postfixExpression = prefixEvaluation.postfixExpression; System.out.println(prefixExression + " = " + infixExpression + " = " + postfixExpression + " = " + answer); } }
  8. This could get confusing. When you want to toggle the on/off state, you have to remember to set both so that you don't have a light that's both on and off. Also, off is implied by not on and so isn't even required.     Your Walk() example is confusing too. It returns true in all cases and isn't clear what's going on. For the see example, using "canSee(object)" would be better as just "see(object)" could imply that you want to look at the object instead.     In your full code example, your functions like "see" and "touch" are doing multiple things and as I said, from their name aren't clear on purpose.
  9. Using trigonometry, you can see the sides as the hypotenuses of right angled triangles. topLeft = (x, y); topRight = (x + (width * cos(angle)), y + (width * sin(angle))); bottomLeft = (x + (height * sin(angle)), y + (height * cos(angle))); bottomRight = (x + (width * cos(angle)) + (height * sin(angle)), y + (width * sin(angle)) + (height * cos(angle))); Edited to fix stupid mistakes.
  10. I don't understand what's going on here.   1) In your first code snippet, you define on and off (Lua uses lowercase true and false keywords), but not light. Saying if (light.on) then is the same as if (light.on == true) then if that's what you're asking. If you mean using if (light == true) then then to me, that's not clear as "light" would be an an instance of a light object. light.on or lightOn would be better.   2) As you're using Lua, you could just write local display = print; This also preserves print's vararg ability.   3) As long as you're clear and consistent (if you use "start" in one place, you use it in all others, and the correct antonym "stop" in all others too), this doesn't matter.   4) What is going on here? Your function won't work properly because only tables, userdata and functions are passed by address in Lua. Also "slowly" is an unclear name.
  11. if(GameState.getState() == "OVERWORLD") For this, I recommend using either an enum or inheritance with polymorphism. You should also try to split your update logic and render. Here's how I'd write this (though without knowing more about your code, I don't know where to put things like the instances of link and camera, so I put them in the game class and use getter methods) // All the different states of the game inherit this and implement the abstract methods public abstract class GameState { public Game game; public GameState(Game game) { this.game = game; } // Returns the next game state (most of the time would be this) public abstract GameState update(); public abstract void render(Graphics2d graphics); } public final class Overworld extends GameState { public Overworld(Game game) { super(game); // Other stuff in here } public GameState update() { // Because the game state doesn't have own these Camera camera = game.getCamera(); Link link = game.getLink(); if (!saveMenu.isOn()) { camera.update(link); } else { link.setState(ActionState.IDLE); } link.update(); link.update(camera); // This is confusing updateNpcs(camera); updateMap(camera, link); game.getInventory().update(); // Maybe all these could be moved into link.update(mapsToAdd, inventory); link.collideWithNpcs(mapsToAdd); link.collide(mapsToAdd); link.collideWithLootDrops(mapsToAdd, inventory); link.attackMonsters(mapsToAdd); return this; } public void render(Graphics2d graphics) { drawLevelData.update(game.getLink().getLevel()); drawMap(graphics, game.getCamera()); drawLink(graphics); drawLinkRelativeToCamera(graphics, camera); levelBar.draw(graphics); drawLevelData.draw(graphics); inventory.draw(graphics); } } public final class Game { private GameState gameState; private final Camera camera; private final Link link; public Game() { gameState = new Overworld(this); } public void update() { gameState = gameState.update(); } public void render() { gameState.render(); } public Camera getCamera() { return camera; } public Link getLink() { return link; } } Edit: I also think you should change "state" to something else, like "world". "State" to me implies loading, main menu, play screen, game over screen, pause screen etc.
  12. I like the way that Django (Python web framework) handles plurals. When you create a database table, you create it as a Python class, e.g. "class Reply(models.Model)". When displaying the reply table contents on the admin site, it guesses the plural by just adding an -s (so "replys"), however you change this by overriding it like this: class Reply(models.Model): class Meta: verbose_name_plural = "Replies"; Android handles this by using quantity strings in resource files; the documentation has a good example of how this works.     I think for your code, what would work best is a system similar to Django's (assume -s unless overriden). Here's an example of these: -- Create a class called object local Object = { }; -- Name is string; pluralName is string or nil function Object.new(name, pluralName) name = tostring(name); -- If no plural name was provided, assume it's just add -s random ' to fix highlighting pluralName = pluralName or name.."s"; local object = { }; object.name = name; object.pluralName = pluralName; -- Requires the metatable for the class system to work setmetatable(object, {__index = Object}); return object; end -- Methods function Object:getName() return self.name; end function Object:getPluralName() return self.pluralName; end -- Dummy methods for testing local function isCollisionBetween(objectA, objectB) return true; end local textSmelling = "SMELLING"; local function setText(type, text) print(type, text); end -- Using it: local Player = { }; function Player:smell(object) if (isCollisionBetween(self, object)) then setText(textSmelling, "I smell "..object:getPluralName()); end end local apple = Object.new("apple"); local cactus = Object.new("cactus", "cacti"); Player:smell(apple); Player:smell(cactus); I tested it on the Lua online demo and got: SMELLING I smell apples SMELLING I smell cacti
  13. The constructor is private because the writer(s) didn't want you to be able to instantiate it. Instead they used static methods. This means that instead of: DDSUtil util = new DDSUtil(); // If the constructor were public and didn't use static methods BufferedImage image = util.read(file); // Requires a try { } catch { } too You should actually write: BufferedImage image = DDSUtil.read(file); // But don't forget try and catch The same applies for all static members - you refer to them by the class name, not an instance of the object.
  14. You can't put code that actually does something like "DayPlanner[0] = new Appointment();" in just the class body. It has to be either in a method or a static block, though that would require DayPlanner to be a static member of Planner.   I'd change it to look like this: public class Planner { private Appointment[] dayPlanner; public Planner(int maxAppointments) { dayPlanner = new Appointment[maxAppointments]; dayPlanner[0] = new Appointment(); } } // Create a planner with 20 appointments new Planner(20); However I would recommend using an ArrayList<Appointment> instead of a fixed array.
  15. I made a flappy bird clone called "Buzzy Bee" in CoffeeScript and HTML5 (because my teacher started making one and I wanted to make a better one :P). The source is on Github: https://github.com/dr01d3k4/BuzzyBee and you can play it on jsfiddle: http://jsfiddle.net/dr01d3k4/BGAD7/   The physics are pretty simple. It doesn't use Box2D because the vertical motion is handled by simple integration and collision is just AABB with no resolution (because instant game over, though may get a bit harder if you want to add a dieing animation). All the code for the game logic is in game_screen.coffee. Here are the important parts (commented to explain) # In the constructor, declare a player object (@ means self/this) # The velocity and acceleration are both vertical, horizontal movement is handled differently @player = x: PLAYER_START_X y: PLAYER_START_Y width: PLAYER_WIDTH height: PLAYER_HEIGHT velocity: 0 acceleration: GRAVITY rotation: 0 # Create the pipes (fixed amount that get recycled) and keep track of where the first one is # The player doesn't actually move horizontally - the pipes do @firstPipeX = START_PIPE_X @pipes = [ ] for i in [0...PIPE_COUNT] @pipes.push @newPipe() # The methods for creating new pipes (as a JavaScript object) randomPipeHeight: -> randomNumberInclusive PIPE_MIN_HEIGHT, SCREEN_HEIGHT - PIPE_MIN_HEIGHT - PIPE_GAP_HEIGHT newPipe: -> baseHeight: @randomPipeHeight(), gapHeight: PIPE_GAP_HEIGHT, width: PIPE_WIDTH, scored: no # When the player jumps, set his velocity to jump velocity and add some rotation for graphical effect playerJump: -> @player.velocity = JUMP_VELOCITY @player.rotation = PLAYER_JUMP_ROTATION # Game logic with some parts like AI and trail taken out update: (deltaTime) -> @time += deltaTime # This is just for graphical effect @player.rotation += PLAYER_ROTATION_SPEED * deltaTime # Jump if the player is pressing the correct keys and is actually playing # (In the menu screen, the game is played by the AI and uses the same code so these @playerControlled checks are common) @playerJump() if @playerControlled and (isKeyPressed(Key.JUMP) or isMousePressed()) # Simple integration vertically @player.velocity += @player.acceleration * deltaTime @player.y += @player.velocity * deltaTime # Check if the player's gone out of bounds if @player.y + @player.height > SCREEN_HEIGHT @player.y = SCREEN_HEIGHT - @player.height @player.velocity = 0 @gameOver() if @playerControlled if @player.y < 0 @player.y = 0 @player.velocity = 0 @gameOver() if @playerControlled # Offset the first pipe by the speed of the player # Negative because player going to right, so pipes must go to the left for same effect @firstPipeX -= MOVE_SPEED * deltaTime # If the first pipe's gone off screen if @firstPipeX < -PIPE_WIDTH * 2 # Remove the first pipe by moving each down for i in [1...@pipes.length] @pipes[i - 1] = @pipes[i] # Add a new pipe # However probably would be more efficient to reuse the first that was removed @pipes[PIPE_COUNT - 1] = @newPipe() # Because the first pipe was removed, need to reset this back to position of new first pipe @firstPipeX += PIPE_WIDTH + PIPE_GAP_HORIZONTAL # Check player not rotating too much @player.rotation = -PLAYER_MAX_ROTATION if @player.rotation < -PLAYER_MAX_ROTATION @player.rotation = PLAYER_MAX_ROTATION if @player.rotation > PLAYER_MAX_ROTATION # Detect collision and scoring x = @firstPipeX collision = no for pipe in @pipes # Only if the left of the pipe is to the left of the player's right (i.e player's going through/gone past) # Ones further on get skipped if x < @player.x + @player.width # If the right of the pipe is on right of player's left # And the player isn't inside the gap # Then collided # Like AABB intersection but y is negated if x + pipe.width > @player.x and (@player.y + @player.height > SCREEN_HEIGHT - pipe.baseHeight or @player.y < SCREEN_HEIGHT - pipe.baseHeight - pipe.gapHeight) collision = yes break # If the right of the pipe is on the left of the player if x + pipe.width <= @player.x # And it hasn't already been used for scoring # Then the player has gone through this pipe and so gains a point @score += 1 unless pipe.scored pipe.scored = yes # Update the pipe's left position to the one of the next pipe x += pipe.width + PIPE_GAP_HORIZONTAL # Player hit a pipe and so game over return @gameOver() if collision and @playerControlled