FlashPunk Forums

September 02, 2010 *
News:
 



Pages: [1] 2 3
  Print  
Author Topic: [FP 1.3] - Open Source Platform Engine  (Read 1458 times)
Noel
===========
****
Posts: 330



View Profile WWW Email
« on: June 13, 2010 »

I haven't had time to complete my Platform tutorial yet, so I thought I would at least post the source code to a simple platform engine I put together today. It's not really commented, but it includes things such as multiple levels, level transition, player movement, jumping, walking, friction, a goal in each level, level resets if you die, etc.

Play: Give it a go!
Source: Download!
Ogmo: Get!

The levels used in this engine were created using Matt Thorson's awesome Ogmo Editor, which is what the "ogmo" link is all about above.

If you want to try something a little more advanced, check out this other platform engine I made!

Cheers,
Noel.
Logged

Portfolio | (I'm Noel, AKA Drazzke)
Ultima2876
+++++++++++
***
Posts: 210

ultima@live.co.uk
View Profile WWW Email
« Reply #1 on: June 13, 2010 »

Good stuff, I'm sure many people will get a lot of this Smiley
Logged
Jack
***********
**
Posts: 50


View Profile
« Reply #2 on: June 13, 2010 »

THANKS  Cool
Logged
getbrett
....................
*
Posts: 11


View Profile Email
« Reply #3 on: June 13, 2010 »

This is brilliant Noel! Thanks. From looking at your source the most important lesson I've learnt is how to properly structure my code (meaning the game I've been working on to learn FlashPunk is horribly messy, mostly because I've been modifying someone else's code rather than writing it myself).

Later tonight I'm going to attempt to modify your code to include support for pixel-perfect collision on sloped tiles.
Logged
jonnythefox
--------------

Posts: 1


View Profile
« Reply #4 on: June 29, 2010 »

This helps, a lot...

It'd be nice if it were a little more commented, but I can mostly tell what's going on. I'm trying to adapt a double jump at the moment, I'll let you know how it goes.
Logged
Gornova81
===========
****
Posts: 271



View Profile WWW
« Reply #5 on: June 29, 2010 »

drazzle!!! Thanks! Cheesy
I've been a little lost with FP1.0 and this can really help me!



This can be usefull:

Here Ogmo Editor (0.908) File Project (save in assets/levels/OPSG.oep):

Code:
<project>
<name>Open Source Platform Engine - Ogmo Editor File Project 0.908</name>
<settings>
<defaultWidth>320</defaultWidth>
<defaultHeight>240</defaultHeight>
<workingDirectory>../graphics</workingDirectory>
</settings>
<tilesets>
<tileset name="wall" image="wall.png" tileWidth="16" tileHeight="16"/>
</tilesets>
<objects>
<object name="wall" image="wall.png" width="16" height="16"/>
<object name="player" image="player.png" width="16" height="16" limit="1"/>
<object name="finish" image="finish.png" width="16" height="16" limit="1"/>
</objects>
<layers>
<objects name="objects" gridSize="16"/>
<tiles name="tiles" gridSize="16" />
</layers>
</project>

and my Level4.oel (save in assets/levels):

Code:
<level>
  <width>320</width>
  <height>240</height>
  <objects>
    <wall x="48" y="48"/>
    <wall x="16" y="48"/>
    <wall x="32" y="48"/>
    <wall x="64" y="48"/>
    <wall x="48" y="48"/>
    <player x="32" y="16"/>
    <wall x="112" y="48"/>
    <wall x="160" y="48"/>
    <wall x="144" y="128"/>
    <wall x="128" y="128"/>
    <wall x="112" y="128"/>
    <wall x="80" y="128"/>
    <wall x="64" y="128"/>
    <wall x="96" y="128"/>
    <wall x="160" y="128"/>
    <wall x="176" y="128"/>
    <wall x="192" y="128"/>
    <wall x="208" y="48"/>
    <wall x="224" y="48"/>
    <wall x="240" y="48"/>
    <wall x="256" y="48"/>
    <wall x="288" y="48"/>
    <wall x="288" y="64"/>
    <wall x="288" y="80"/>
    <wall x="288" y="96"/>
    <wall x="272" y="112"/>
    <wall x="256" y="128"/>
    <wall x="272" y="128"/>
    <wall x="304" y="128"/>
    <wall x="288" y="112"/>
    <wall x="288" y="128"/>
    <wall x="288" y="144"/>
    <wall x="288" y="160"/>
    <wall x="288" y="176"/>
    <wall x="288" y="192"/>
    <wall x="288" y="208"/>
    <wall x="288" y="208"/>
    <wall x="288" y="224"/>
    <wall x="272" y="224"/>
    <wall x="304" y="224"/>
    <wall x="0" y="48"/>
    <wall x="0" y="32"/>
    <wall x="0" y="16"/>
    <wall x="0" y="0"/>
    <wall x="0" y="64"/>
    <wall x="0" y="80"/>
    <wall x="0" y="144"/>
    <wall x="0" y="112"/>
    <wall x="0" y="96"/>
    <wall x="0" y="128"/>
    <wall x="0" y="160"/>
    <wall x="0" y="176"/>
    <wall x="0" y="208"/>
    <wall x="0" y="192"/>
    <wall x="0" y="224"/>
    <wall x="16" y="224"/>
    <wall x="48" y="224"/>
    <wall x="32" y="224"/>
    <wall x="112" y="224"/>
    <wall x="80" y="224"/>
    <wall x="64" y="224"/>
    <wall x="96" y="224"/>
    <wall x="256" y="224"/>
    <wall x="240" y="224"/>
    <wall x="240" y="224"/>
    <wall x="240" y="224"/>
    <wall x="224" y="224"/>
    <wall x="224" y="224"/>
    <wall x="208" y="224"/>
    <wall x="192" y="224"/>
    <wall x="192" y="224"/>
    <wall x="176" y="224"/>
    <wall x="160" y="224"/>
    <wall x="144" y="224"/>
    <wall x="144" y="224"/>
    <wall x="144" y="224"/>
    <wall x="128" y="224"/>
    <finish x="144" y="192"/>
    <wall x="112" y="176"/>
    <wall x="144" y="176"/>
    <wall x="176" y="176"/>
    <wall x="224" y="176"/>
    <wall x="48" y="176"/>
  </objects>
</level>

to add your levels into game:

* open Game.as
* find:

Code:
[Embed(source = '../assets/levels/Level1.oel', mimeType = "application/octet-stream")] public var level1:Class;

* add

Code:
[Embed(source = '../assets/levels/Level4.oel', mimeType = "application/octet-stream")] public var level4:Class;

* find

Code:
public var levels:Array = new Array(level1, level2, level3);

* change in:

Code:
public var levels:Array = new Array(level1, level2, level3,level4);

have fun in design levels Cheesy

Logged

Shroom
....................
*
Posts: 36



View Profile
« Reply #6 on: June 29, 2010 »

After using and modifying your Platforming Tutorial, I have a few additions I've thrown in. The main two (which I'd like to share) are an air control variable and one-way platforms.

I made a platforming game of my own a few years ago in GameMaker, and it turns out that you tend to do a lot of things the same way I had done. (I found your collision detection to be more effective in this case than the way I had done in GameMaker, however.)

This is kind of going out to everyone, by the way, meant as sort of an addition.

EDIT: I updated the actual engine files and posted them in a later post. The rest of this post is mostly pointless now, but it's still is an okay explanation I guess.

Anyway, air control is very simple. One-way platforms are a little trickier. I'll start with air control.

First, these are the player variables I'm using:

Code:
private var xVelocity:Number    = 0;
private var yVelocity:Number    = 0;
private var acceleration:Number = 1;
private var friction:Number     = 2;
private var maxXVelocity:Number = 8;
private var jump:Number         = 20;
private var gravity:Number      = 2;
private var maxFall:Number      = 20;
private var airControl:Number   = .5;
private var onGround:Boolean    = false;

They're mostly the same (though some have different names), but I've added maxFall, airControl, and onGround. onGround is fairly self-explanatory. maxFall is just another limit for the player's speed. Including this can give the player a lot more control over the character, which I always think is important. I almost always set this to the same as the jump variable. airControl is the percentage of control the player gets when in the air. I find that 50% airControl feels natural (even though 0% is the most natural). Removing perfect air control, interestingly, can actually give players more control by allowing them to slow their jumps instead of simply changing direction. I've found that there are few situations where the player needs to change direction mid-air, but many where the player needs to adjust mid-air speed. By the way, the values I'm using are for 64x64 sprites. That just happens to be the size of the sprites I'm planning to eventually use. Until then I've just scaled up Noel's sprites.

The first thing I do in the update function is update onGround.

Code:
//ground check
onGround = collide("wall", x, y + 1);

All this actually does is make the code a little cleaner. It will also help a lot when we add one-way platforms.

Now, in the left and right checks, we just make a second check to see if the player is on the ground. If it isn't, multiply by airControl.

Code:
//moving left
if (Input.check("left")) {
sprPlayer.flipped = true;
sprPlayer.play("move");
if (onGround) {
xVelocity -= acceleration;
}
else {
xVelocity -= acceleration * airControl;
}
}
//moving right
if (Input.check("right")) {
sprPlayer.flipped = false;
sprPlayer.play("move");
if (onGround) {
xVelocity += acceleration;
}
else {
xVelocity += acceleration * airControl;
}
}

One little note here is that I'm using sprite mirroring. Depending on your sprites, you may or may not want this.

That's all there is to airControl. Be sure to test it before moving on. Next is one-way platforms. These are actually very easy. We only need to create them, and make a couple of extra checks.

Step one is making them. I'm calling them ledges. The name isn't important.

Code:
package {
/**
* ...
* @author Van Phelan
*/

import net.flashpunk.Entity;

public class Ledge extends Entity {

public function Ledge() {
setHitbox(64, 1, 0, 0);
type = "ledge";
}

}

}
They're identical to ordinary walls, except for their type and their hitbox. We only want to check collisions with the topmost pixels. Once again, my sprites are 64x64. Oh, and don't forget to include the sprite for ledges.

Next, we modify our onGround update to include ledges.

Code:
//ground check
onGround = (collide("wall", x, y + 1) || (collide("ledge", x, y + 1) && !collide("ledge", x, y)));

Now, in addition to checking if you're on a wall, it will check to see if you're on a ledge, but not inside the ledge. Normally, when you check if something is one pixel under you, it will check if it is under each of your pixels. That's why we have to also make sure the ledge doesn't intersect the player. Otherwise we can get stuck halfway through the ledge.

The only thing left is modifying vertical collision-checks. I have it formatted differently than Noel, but its otherwise about the same. I've also added in checks for our ledges.

Code:
//vertical motion and collisions
for (i = 0; i < Math.abs(yVelocity); i++) {
if (!collide("wall", x, y + FP.sign(yVelocity)) && !(collide("ledge", x, y + 1) && yVelocity > 0 && !collide("ledge", x, y))) {
y += FP.sign(yVelocity);
}
else {
yVelocity = 0;
break;
}
}

The ledge check is about the same as the wall check, but we want to make sure that it is under us (it's one-way, remember?), that we're actually moving downward, and that we aren't intersecting the ledge.

That's all their is to it, but if you want to also allow the player to drop from the platform, you can do so like this:

Code:
//dropping
if (Input.check("down") && Input.pressed("jump")) {
if (collide("ledge", x, y + 1) && !collide("wall", x, y + 1)) {
y++;
}
}
//jumping
else if (Input.pressed("jump") && onGround) {
yVelocity = -jump;
}

All dropping does is move you one pixel down. Since the ledge's hitbox is only one pixel, and our collision checks don't include intersections, this moves us completely through it. I put the dropping code before jumping so that jumping can become an else if. That way you won't accidentally jump in some cases. I used Input.pressed() for jumping, but Input.check() should work just as well.

Result: http://flashgamedojo.com/u/7sdm7t/

I probably forgot to mention a hundred things, so yeah, if this doesn't work, let me know.

Oh, and I forget to mention jump is the spacebar.
Logged
Gornova81
===========
****
Posts: 271



View Profile WWW
« Reply #7 on: June 30, 2010 »

maybe you can share source code?
Logged

Shroom
....................
*
Posts: 36



View Profile
« Reply #8 on: June 30, 2010 »

I had a bunch of other junk in my source, so I didn't want to post the entire thing, but I'll go ahead and add this stuff to Noel's Player class and post that.
Logged
Gornova81
===========
****
Posts: 271



View Profile WWW
« Reply #9 on: June 30, 2010 »

Noel, maybe it's better (IMHO) have a class that is affected by gravity and player (or crates or something else) can extend it? (or compose as you wish?)

I've done it yesterday, if you want, I'll post my code!
Logged

Shroom
....................
*
Posts: 36



View Profile
« Reply #10 on: June 30, 2010 »

Okay, here is Player.as with all the changes I've made.

Code:
package src
{
import flash.geom.Point;
import net.flashpunk.Entity;
import net.flashpunk.FP;
import net.flashpunk.graphics.Image;
import net.flashpunk.graphics.Spritemap;
import net.flashpunk.utils.Input
import net.flashpunk.utils.Key;
/**
* ...
* @author Noel Berry
*/
public class Player extends Entity
{

[Embed(source = '../assets/graphics/player.png')] private var imgPlayer:Class;
public var sprPlayer:Spritemap = new Spritemap(imgPlayer, 16, 16);

public var speed:Point = new Point(0, 0);
public var acceleration:Number = 0.5;
public var friction:Number = 0.5;
public var gravity:Number = 0.2;
public var jump:Number = 3.6;
public var maxspeed:Number = 2;
public var maxFall:Number = 3.6;
public var airControl:Number = .5; //Percentage of control when airborne. Should be between 0 and 1 (inclusive).
public var onGround:Boolean = false;

public var frozen:Boolean = false;

public function Player(x:int,y:int)
{
this.x = x;
this.y = y;

sprPlayer.add("standRight", [0], 0, false);
sprPlayer.add("standLeft", [4], 0, false);
sprPlayer.add("walkRight", [0, 1, 2, 3], 0.2, true);
sprPlayer.add("walkLeft", [4, 5, 6, 7], 0.2, true);

sprPlayer.play("standRight");
graphic = sprPlayer;

Input.define("left", Key.LEFT);
Input.define("right", Key.RIGHT);
Input.define("down", Key.DOWN);
Input.define("jump", Key.Z, Key.UP);

setHitbox(16, 16);
}

override public function update():void
{
if (frozen) { return; }

//ground check
  onGround = (collide("Wall", x, y + 1) || (collide("Ledge", x, y + 1) && !collide("Ledge", x, y)));

//move left
if (Input.check("left"))
{
sprPlayer.play("walkLeft");
if (onGround) { speed.x -= acceleration; }
else { speed.x -= acceleration * airControl; }
}
//move right
if (Input.check("right"))
{
sprPlayer.play("walkRight");
if (onGround) { speed.x += acceleration; }
else { speed.x += acceleration * airControl; }
}
//drop
if (Input.check("down") && Input.check("jump"))
{
if (collide("Ledge", x, y + 1) && !collide("Wall", x, y + 1)) { y++; }
}
//jump
else if (Input.check("jump") && onGround) { speed.y = - jump; }
if (!Input.check("jump") && speed.y < 0) { speed.y += gravity; }

//falling
speed.y += gravity;

//horizontal collisions
for (var i:int = 0; i < Math.abs(speed.x); i += 1)
{
if (!collide("Wall", x + FP.sign(speed.x), y))
{
x += FP.sign(speed.x);
} else
{
if (speed.x > 0) { sprPlayer.play("standRight"); } else { sprPlayer.play("standLeft"); }

speed.x = 0;
}
}
//vertical collisions
for (i = 0; i < Math.abs(speed.y); i += 1)
{
if (!collide("Wall", x, y + FP.sign(speed.y)) && !(collide("Ledge", x, y + 1) && speed.y > 0 && !collide("Ledge", x, y)))
{
y += FP.sign(speed.y);
} else { speed.y = 0; }
}

//idling
if ((!Input.check("left") && !Input.check("right")) || (Input.check("left") && Input.check("right")))
{
if (speed.x > 0)
{
sprPlayer.play("standRight");
//For friction when in the air, leave the next line uncommented.
//speed.x -= friction;
//For no friction in the air, comment the above line and uncomment the next. (Recommended when airControl < 1)
if (onGround) { speed.x -= friction; }
if (speed.x < 0) { speed.x = 0; }
}
if (speed.x < 0)
{
sprPlayer.play("standLeft");
//For friction when in the air, leave the next line uncommented.
//speed.x += friction;
//For no friction in the air, comment the above line and uncomment the next. (Recommended when airControl < 1)
if (onGround) { speed.x += friction; }
if (speed.x > 0) { speed.x = 0; }
}
}

//speed limits
if (Math.abs(speed.x) > maxspeed) { speed.x = FP.sign(speed.x) * maxspeed; }
if (speed.y > maxFall) { speed.y = maxFall; }

if (collide("Finish", x, y)) {
frozen = true;
Game.finished = true;
}

//camera
FP.camera.x += Math.round(((x - FP.width / 2) - FP.camera.x) / 10);
FP.camera.y += Math.round(((y - FP.height / 2) - FP.camera.y) / 10);

if (y > FP.height * 4) { Game.restart = true; frozen = true; }
}

}

}

Changelog:
Added maxFall, airControl, and onGround.
Added initial onGround collision check.
Updated some collision checks to use onGround.
Added onGround checks to movement for air control.
Changed the idling check to include when the player is holding both left and right, in addition to neither left nor right.
Added an option to remove friction when in the air. (Best used with limited air control.)
Added a check for falling speed.
Updated vertical collision check to include one-way platforms.
Added the ability to drop through platforms.

Ledge.as...

Code:
package src
{
import flash.geom.Rectangle;
import flash.sampler.NewObjectSample;
import net.flashpunk.Entity;
import net.flashpunk.graphics.Image;
/**
* ...
* @author Noel Berry
*/
public class Ledge extends Entity
{

[Embed(source = '../assets/graphics/ledge.png')] private var imgLedge:Class;
public var sprLedge:Image = new Image(imgLedge, new Rectangle(0, 0, 16, 16));

public function Ledge(x:int,y:int)
{
this.x = x;
this.y = y;

type = "Ledge";
graphic = sprLedge;
setHitbox(16, 1);
}

}

}

A single line was added to Game.as as well.

Code:
//add the objects to level
for each (o in xml.objects[0].player) { add(player = new Player(o.@x, o.@y)); }
for each (o in xml.objects[0].wall) { add(e = new Wall(o.@x, o.@y)); }
for each (o in xml.objects[0].ledge) { add(e = new Ledge(o.@x, o.@y)); }
for each (o in xml.objects[0].finish) { add(e = new Finish(o.@x, o.@y)); }

The the Ogmo Project file was updated.

Code:
<objects>
<object name="wall" image="wall.png" width="16" height="16"/>
<object name="ledge" image="ledge.png" width="16" height="16"/>
<object name="player" image="player.png" width="16" height="16" limit="1" imageWidth="16" imageHeight="16"/>
<object name="finish" image="finish.png" width="16" height="16" limit="1"/>
</objects>

And finally, here's the ledge sprite I used.



Hopefully I didn't forget anything.

Result: http://flashgamedojo.com/u/1r10yx/

Project Files: http://www.mediafire.com/file/yyv05mktjoh/OSPG.zip
Logged
Gornova81
===========
****
Posts: 271



View Profile WWW
« Reply #11 on: June 30, 2010 »

cool! I like it, but is a bit difficult on level2 jump -> move on air result? dooown Sad
Do you have same problem?

Maybe we can figure how to separate player / wall class from their images? Ogmo can handle this?
Logged

Shroom
....................
*
Posts: 36



View Profile
« Reply #12 on: June 30, 2010 »

cool! I like it, but is a bit difficult on level2 jump -> move on air result? dooown Sad
Do you have same problem?

Maybe we can figure how to separate player / wall class from their images? Ogmo can handle this?

I realized after adding it that limiting air control makes it trickier when you can reach your maximum speed this quickly, especially since before, you could just let go of the button and drop strait down. Embarrassed The option is still something people may want to include for other types of platformers.

I'm not completely sure what problem you're referring to, though, other than the difficulty. Is it the increased time before respawning? That would be because of the maximum fall speed. It just takes longer to reach the bottom.
Logged
Noel
===========
****
Posts: 330



View Profile WWW Email
« Reply #13 on: June 30, 2010 »

Glad you guys found this so useful. Awesome additions/modifications as well, Shroom Cheesy

@jonnythefox I'll see if I can comment a few extra things and upload an updated version in a few days.
Logged

Portfolio | (I'm Noel, AKA Drazzke)
Gornova81
===========
****
Posts: 271



View Profile WWW
« Reply #14 on: July 01, 2010 »

If i have some time this weekend I'll build an example with movable crates Cheesy
Logged

Pages: [1] 2 3
  Print  
 
Jump to:  

Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC