Fisica Platformer
In the first part of this tutorial we went over the platformer genre, how apparent it is in the gaming industry, and we began making the very basics of our own in Processing using the Fisica library. By the end of the tutorial we had our player able to move and jump around on a surface. In this tutorial we’ll be adding an entity that will roam around randomly and give our player the ability to shoot it.
Final note before the tutorial, all code in this tutorial will be added into the final code from part 1 of this tutorial series.
Firstly, we’ll need to add two ArrayList objects to store the enemies along with the bullets shot by the player. The reasoning for using an ArrayList is that the size/length of these lists will vary. The snippet of code down below will demonstrate how to declare and instantiate the two ArrayList objects.
1 2 3 4 5 6 |
ArrayList<FBox> enemies = new ArrayList<FBox>(); ArrayList<FBox> bullets = new ArrayList<FBox>(); void setup() { ... } |
Now that we have the means of storing our enemies and bullets, we’ll add our enemies. We’ll start by adding a “addEnemy” function with two parameters, the desired x & y position of our enemy (both are floats.) The function will create an FBox named enemy and set its position to the ones given in the arguments. We will then set the color of the enemy to bright red using “setFill” and reducing the friction to ensure the enemies move smoothly using “setFriction”. After our enemies are made with the given details we will add them to the enemies ArrayList and then add it to the FWorld. The resulting code is below.
1 2 3 4 5 6 7 8 9 |
void addEnemy(float x, float y) { FBox enemy = new FBox(30, 30); //Adds enemy as FBox enemy.setRotatable(false); enemy.setPosition(x, y); //Sets position to given x or y enemy.setFriction(0); enemy.setFill(255, 0, 0); //Sets enemy to red enemies.add(enemy); //Adds enemy to world & array world.add(enemies.get(enemies.size()-1)); //^ (Uses "enemies.size() - 1" to add most recent enemy) } |
We’ll make the movement of our enemies considerably random by creating a “moveEnemies” function which will just loop through the enemies ArrayList and using addForce we’ll add a random force making them move left or right.
1 2 3 4 5 6 |
void moveEnemies() { //Randomly moves enemies for (int i=0; i<enemies.size(); i++) { enemies.get(i).addForce(random(-250, 250), 0); } } |
Now that we have the basics down for our enemies it’s a lot more useful if we actually used the code we just wrote. We’ll add “addEnemy(random(width), height / 8);” to our setup function along with the other “world.add” lines. This will add an enemy near the top of the screen in a random x-position that will fall down to the platform with our player. Next, we’ll simply add “moveEnemies” above “world.step()” in our draw function. Now our program should have one red square enemy which roams around with our player.
To finish off this part of the tutorial we’ll be implementing bullets, collision between bullets & enemies, and tweaking the “platformCheck” function.
To add the bullets we’ll create a “addBullet(float x, float y)” function. Then we’ll add Processing’s built-in “mousePressed” function which will call our “addBullet” function with the mouse x & y as the arguments.
1 2 3 4 5 6 |
void mousePressed() { addBullet(mouseX, mouseY); //Adds bullet upon mouse being pressed } void addBullet (float x, float y) { } |
Now the obvious issue with “addBullet” is that it’s empty. Firstly, we’ll make two float variables named “heightDiff” and “widthDiff” which will equal the distance between the mouse’s x & y positions and the player’s. We’ll then create an FBox named “bullet” and give it a radius of 5. Then will use Fisica’s “setBullet(true)” as it gives fast-moving objects more accurate calculations. Then we’ll add an if-else statement that checks which side of the player the mouse is on two make the bullet come out of that side of the player. Then we’ll set the bullets x & y velocities to the width & height difference variables multiplied by 15 to give the bullets a fast speed in the direction of the mouse. Lastly, just like the addEnemy function we’ll add the bullet to the bullets ArrayList and the world. The code should appear as down below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void addBullet (float x, float y) { float heightDiff = mouseY - player.getY(); //Calculates difference between mouse position and player's float widthDiff = mouseX - player.getX(); //^ FBox bullet = new FBox(5, 5); //Creates bullet with radius of 5 bullet.setBullet(true); //Setting an FBox as a bullet is ideal for fast moving objects, more accurate calculations //Makes bullet appear on left or right side of player depending on mouseX if (mouseX < player.getX()) { bullet.setPosition(player.getX() - 20, player.getY()); } else { bullet.setPosition(player.getX() + 20, player.getY()); } bullet.setVelocity(widthDiff * 15, heightDiff * 15); //Sets velocity bases on mouse position bullets.add(bullet); //Adds bullet to array world.add(bullets.get(bullets.size()-1)); //adds bullet to world } |
Now that we’ve added our enemies and bullets we’ll add a function named “bulletCheck()” which will loop through our bullets, check if they’re touching anything with “getTouching()” and if so, loop through our enemies and then use “getTouching()” and if it returns true, remove the bullet & enemy from the world and their arrays.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void bulletCheck() { //Loops through all bullets for (int i=0;i<bullets.size();i++) { //Checks if bullet is making contact with any objects if (bullets.get(i).getTouching().size() > 0) { //Loops through enemies for (int j=0; j<enemies.size(); j++) { //Checks if bullet is touching an enemy if (bullets.get(i).getTouching().contains(enemies.get(j))) { //If so removes enemy from world & array world.remove(enemies.get(j)); enemies.remove(j); addEnemy(random(width), 20); //Adds new enemy } } //Removes bullet from world & array once it makes contact with anything world.remove(bullets.get(i)); bullets.remove(i); } } } |
We’ll then call this function in our draw function so it runs every frame. The last thing we’ll be doing in this part of the tutorial is changing the way platformCheck() works to base if off of the players y-velocity rather then whether or not they’re touching the platform. This will allow the function to work if you were to add other platforms or obstacles. This is a fairly simple change which can be seen below.
1 2 3 4 5 6 7 8 |
void platformCheck() { //Keeps track of if the player touching platform for ease of access if (player.getVelocityY() < 0.001 && player.getVelocityY() > -0.001) { onGround = true; } else { onGround = false; } } |
We should now have a platformer with a player able to move around and shoot, with enemies who roam around randomly. Now with the basics over, you should challenge yourself to add more to it. Some ideas could be adding more enemies and make them chase the player, add a score for killing enemies and make them respawn or make the player have health so the game could end. The complete code can be found down below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
//Declares Fisica variables FWorld world; FBox platform; FBox player; ArrayList<FBox> enemies = new ArrayList<FBox>(); ArrayList<FBox> bullets = new ArrayList<FBox>(); boolean left, right, onGround; void setup() { size(480, 360); //Creates canvas Fisica.init(this); //Initiates Fisica library world = new FWorld(); //Instantiates world as FWorld() world.setEdges(); //Sets edges of the screen to physical border player = new FBox(30, 30); player.setPosition(width / 2, height / 8); platform = new FBox(width, 30); platform.setPosition(width / 2, height * 0.8); platform.setStatic(true); //Sets "platform" to static (won't be affected by forces such as gravity addEnemy(random(width), height / 8); //Adds content to world. world.add(player); world.add(platform); } void draw() { background(30); //Sets background to grey, clears previous content fill(255); text("A & D to move left & right", 10, 15); text("Space to jump", 10, 30); platformCheck(); //Detects whether or not player is touching platform. bulletCheck(); world.step(); //Moves "world" forward by 1/60th of a second world.draw(); //Draws all content contained in "world" if (left) { player.setVelocity(-100, player.getVelocityY()); //Moves player left } if (right) { player.setVelocity(100, player.getVelocityY()); //Moves player right } } void platformCheck() { //Keeps track of if the player touching platform for ease of access if (player.getVelocityY() < 0.001 && player.getVelocityY() > -0.001) { onGround = true; } else { onGround = false; } } void keyPressed() { //Keeps track if player is moving left or right if (key == 'a' || key == 'A') { left = true; } if (key == 'd' || key == 'D') { right = true; } if (key == ' ' && onGround) { player.addForce(0, - (player.getMass() * 10000)); //Makes player jump } } void keyReleased() { //Keeps track if player is going left & right, if not it slows down player quickly if (key == 'a' || key == 'A') { left = false; player.setVelocity(player.getVelocityX() * 0.5, player.getVelocityY()); //Reduces player's velocity by half } if (key == 'd' || key == 'D') { right = false; player.setVelocity(player.getVelocityX() * 0.5, player.getVelocityY()); //Reduces player's velocity by half } } void addEnemy(float x, float y) { FBox enemy = new FBox(30, 30); //Adds enemy as FBox enemy.setRotatable(false); enemy.setPosition(x, y); //Sets position to given x or y enemy.setFriction(0); enemy.setFill(255, 0, 0); //Sets enemy to red enemies.add(enemy); //Adds enemy to world & array world.add(enemies.get(enemies.size()-1)); //^ (Uses "enemies.size() - 1" to add most recent enemy) } void moveEnemies() { //Randomly moves enemies for (int i=0; i<enemies.size(); i++) { enemies.get(i).addForce(random(-250, 250), 0); } } void mousePressed() { addBullet(mouseX, mouseY); //Adds bullet upon mouse being pressed } void addBullet (float x, float y) { float heightDiff = mouseY - player.getY(); //Calculates difference between mouse position and player's float widthDiff = mouseX - player.getX(); //^ FBox bullet = new FBox(5, 5); //Creates bullet with radius of 5 bullet.setBullet(true); //Setting an FBox as a bullet is ideal for fast moving objects, more accurate calculations //Makes bullet appear on left or right side of player depending on mouseX if (mouseX < player.getX()) { bullet.setPosition(player.getX() - 20, player.getY()); } else { bullet.setPosition(player.getX() + 20, player.getY()); } bullet.setVelocity(widthDiff * 15, heightDiff * 15); //Sets velocity bases on mouse position bullets.add(bullet); //Adds bullet to array world.add(bullets.get(bullets.size()-1)); //adds bullet to world } void bulletCheck() { //Loops through all bullets for (int i=0;i<bullets.size();i++) { //Checks if bullet is making contact with any objects if (bullets.get(i).getTouching().size() > 0) { //Loops through enemies for (int j=0; j<enemies.size(); j++) { //Checks if bullet is touching an enemy if (bullets.get(i).getTouching().contains(enemies.get(j))) { //If so removes enemy from world & array world.remove(enemies.get(j)); enemies.remove(j); addEnemy(random(width), 20); //Adds new enemy } } //Removes bullet from world & array once it makes contact with anything world.remove(bullets.get(i)); bullets.remove(i); } } } |