Sign in to follow this  
khleug35

P2 Platformer Template

Recommended Posts

A Simple Game example of platformer template created using the Panda2 engine + P2 plugin. 

This Template is remade base on Construct 3 platformer template.

Demo Game

Download:(not include p2.js plugin)

https://github.com/SuperFranco16/p2-platformer-template

Features:

-Arrow keys or WASD to move
-P key to pause the game      
-You can jump up across the brown walls

nviFMh5.png

I3WrROz.png

 

Update(2019-9-18):

Added Rope-Swinging Features:

-Jump up and Press E to climb up the rope, when you see red circle.

giphy.gif

Notes:

-This template is not created by enpu, so my code maybe not good or more hardcode or not the best solution to achieve , Welcome to give me a feedback to improve my coding skill, Thanks
-P2 Plugin is not open source, so the download file is not include p2.js.

Full Code: Main.js

game.module(
    'game.main'
)
.require(
    'plugin.p2'
)
.body(function() {  
	game.addAsset('player.png');
	game.addAsset('BackgroundTile.png');
	game.createScene('Main', {
		backgroundColor: '#272727',
		init: function() {
			var ths = this;
			this.world = new game.Physics({
				gravity: [0, 12],
			});
			this.world.on('beginContact', function(data) {
            // Before collision
            });
            this.world.on('impact', function(data) {
            // Collision
            });
            this.world.on('postStep', function(data) {
            // After collision
            });
			//Group
			this.groups = {
				PlayerGroup: Math.pow(2, 0),
				PlayerGroupFilter: Math.pow(2, 1),
				GROUND: Math.pow(2, 2),
			};
			this.container = new game.Container();
			this.container.addTo(this.stage);
			
            //Map     
			this.bg = new game.TilingSprite('BackgroundTile.png', game.width, game.height).addTo(this.container);
			new game.Wall_Platform(32, 96, 32, 672);
			new game.Wall_Platform(160, 384, 32, 224);
			new game.Wall_Platform(1247, 0, 64, 1023);
			new game.Slope_Solid_Platform(675, 310, 200, 32, 8.9);
			new game.Slope_Solid_Platform(619, 463, 365, 32, 9.6);
			new game.Slope_Solid_Platform(322, 392, 219, 32, 9.8);
			new game.Solid_Platform(32, 768, 352, 128);
			new game.Solid_Platform(864, 800, 96, 32);
			new game.Solid_Platform(992, 928, 128, 32);
			new game.Solid_Platform(1088, 736, 96, 32);
			new game.Solid_Platform(992, 928, 128, 32);
			new game.Solid_Platform(1088, 736, 96, 32);
			new game.Solid_Platform(480, 240, 96, 30);
			new game.Solid_Platform(528, 432, 96, 32);
			new game.Solid_Platform(192, 320, 96, 32);
			new game.Solid_Platform(0, -28, 1280, 28);
			new game.Solid_Platform(32, 768, 352, 128);
			new game.JumpThru_Platform(1056, 624, 160, 16);
			new game.JumpThru_Platform(1056, 528, 160, 16);
			new game.JumpThru_Platform(160, 224, 96, 16);
			new game.JumpThru_Platform(320, 160, 96, 16);
			new game.JumpThru_Platform(528, 128, 96, 16);
			new game.JumpThru_Platform(1056, 128, 96, 16);
			new game.MovingSolid_Platformer_Horizon(450, 800, 128, 32, 730, 2000);
			new game.MovingSolid_Platformer_Horizon(640, 160, 128, 32, 800, 2000);
			new game.MovingSolid_Platformer_Vertical(928, 417, 96, 16, 190, 2000);
			
            //How To Play
			new game.Dialogue(42, 790, '-Arrow keys or WASD to move');
			new game.Dialogue(42, 824, '-P key to pause the game');
			new game.Dialogue(42, 858, '-You can jump up across the brown walls');
			
            //Player
			this.player = new game.Player(128, 684);
			
            //Hide BackScreen
			this.blackscreen = new game.Graphics();
			this.blackscreen.fillColor = '#000000';
			this.blackscreen.alpha = 0;
			this.blackscreen.drawRect(0, 0, game.width, game.height);
			
            //Camera Follow Player
			this.camera = new game.Camera(this.player.sprite);
			this.camera.position.set(this.player.sprite.position.x,this.player.sprite.position.y);
			this.camera.limit.x = 0;
			this.camera.limit.y = 0;
			this.camera.limit.width = game.width;
			this.camera.limit.height = game.height;
			this.camera.acceleration = 20;
			this.camera.addTo(this.container);
			this.container.scale.x = 2;
			this.container.scale.y = 2;
		},
		keydown: function(key) {
			if (key === 'P' && this.player.sprite.onScreen()) {
				if (this.paused) {
					this.resume();
					this.blackscreen.remove();
				} else {
					this.pause();
					this.blackscreen.addTo(this.stage);
					this.blackscreen.alpha = 0.5;
				}
			}
		},
		update: function() {
			this.bg.x = this.camera.position.x * 0.4;
		}
	});
	game.createClass('Player', {
		classname: 'playerclass',
		speed: 3,
		alive: true,
                 wall: false,
		isOnGround: false,
		init: function(x, y) {
			var ths = this;
			this.sprite = new game.Sprite("player.png");
			this.sprite.position.set(x, y);
			this.sprite.anchorCenter();
			this.sprite.addTo(game.scene.container);
			this.body = new game.Body({
				mass: 1,
				fixedRotation: true,
				position: [
					this.sprite.position.x / game.scene.world.ratio,
					this.sprite.position.y / game.scene.world.ratio
				],
			});
			this.shape = new p2.Box({
				width: this.sprite.width / game.scene.world.ratio,
				height: this.sprite.height / game.scene.world.ratio
			});
			this.shape.collisionGroup = game.scene.groups.PlayerGroup;
			this.shape.collisionMask = game.scene.groups.PlayerGroup | game.scene.groups.GROUND;
			this.body.addShape(this.shape);
			this.body.collisionGroup = game.Body.PlayerBody;
			this.body.addTo(game.scene.world);
		},
		checkIfCanJump: function() {
			for (var i = 0; i < game.scene.world.narrowphase.contactEquations.length; i++) {
				var c = game.scene.world.narrowphase.contactEquations[i];
				if (c.bodyA === this.body || c.bodyB === this.body) {
					this.isOnGround = true;
					var d = c.normalA[1];
					if (c.bodyB === this.body) d *= -1;
					if (d > 0.5) {
						return true;
					}
				}
			}
			this.isOnGround = false;
			return false;
		},
		Onslope: function() {
			var ths = this;
			for (var i = 0; i < game.scene.world.narrowphase.contactEquations.length; i++) {
				var c = game.scene.world.narrowphase.contactEquations[i];
				if (game.scene.player.body.velocity[1] < 0 && ((c.bodyA === this.body && c.bodyB.collisionGroup === game.Body.SlopePlatformBody) || (c.bodyB === this.body && c.bodyA.collisionGroup === game.Body.SlopePlatformBody))) {
					this.body.mass = 0;
					this.body.velocity[1] = 0;
					var d = c.normalA[1];
					if (c.bodyB === this.body) d *= -1;
					if (d > 0.5) {
						return true;
					}
				}
			}
			this.body.mass = 1;
			return false;
		},
		OnWall: function() {
			var ths = this;
             if(this.wall){    
             game.Timer.add(150, function() {
              ths.wall= false;
             });    
            if (this.sprite.scale.x == -1) {
                        //this.body.velocity[0] = 16; 
                        this.body.position[0] = this.body.position[0] + 0.06;    
                        }else{
                        //this.body.velocity[0] = -16;  
                        this.body.position[0] = this.body.position[0] - 0.06;    
                        }
                        this.body.velocity[1] = -6;
     
            }
            
			for (var i = 0; i < game.scene.world.narrowphase.contactEquations.length; i++) {
				var c = game.scene.world.narrowphase.contactEquations[i];
				if ((c.bodyA === this.body && c.bodyB.collisionGroup === game.Body.WallBody) || (c.bodyB === this.body && c.bodyA.collisionGroup === game.Body.WallBody)) {
                    if(!this.isOnGround){ 
					  if (game.scene.player.body.velocity[1] > 2 && game.keyboard.down('W')) {
                         this.wall = true;  
					    return true;
                       }
                     }
				  }
			}
 
			return false;
		},
		play: function(anim) {
			if (this.sprite.currentAnim === this.sprite.anims[anim]) return;
			this.sprite.play(anim);
		},
		destroy: function() {
			game.scene.blackscreen.addTo(game.scene.stage);
			this.sprite.remove();
			this.body.remove();
			game.scene.removeObject(this)
			var tween1 = new game.Tween(game.scene.blackscreen);
			tween1.to({
				alpha: 1
			}, 1000);
			tween1.easing('Quadratic.InOut');
			tween1.start();
			tween1.onComplete(function() {
				game.system.loadScene('Main');
			});
		},
		update: function() {
			var ths = this;
                  console.log(this.wall); 
            
			if (!this.sprite.onScreen()) {
				this.destroy();
			}
			if (this.checkIfCanJump() || game.scene.player.body.velocity[1] > 2) {
				if (game.keyboard.down('S')) {
					ths.shape.collisionGroup = game.scene.groups.PlayerGroupFilter;
				} else {
					ths.shape.collisionGroup = game.scene.groups.PlayerGroup;
				}
			} else if (game.scene.player.body.velocity[1] < 2) {
				ths.shape.collisionGroup = game.scene.groups.PlayerGroupFilter;
			}
			this.OnWall();
			this.sprite.position.x = this.body.position[0] * game.scene.world.ratio;
			this.sprite.position.y = this.body.position[1] * game.scene.world.ratio;
			this.sprite.rotation = this.body.angle;
			this.Onslope();
            
            if(!this.wall){   
			if (game.keyboard.down('A')) {
				this.body.velocity[0] = -this.speed;
				this.sprite.scale.x = -1;
			} else if (game.keyboard.down('D')) {
				this.body.velocity[0] = this.speed;
				this.sprite.scale.x = 1;
			} else {
			    this.body.velocity[0] = 0;
			}
            }    
			//Jump
			if (this.checkIfCanJump()) {
				if (game.keyboard.down('W')) {
					this.isonSlope = false;
					this.body.velocity[1] = -6;
				}
			  }
        },
	});
	game.createClass('Wall_Platform', {
		init: function(x, y, width, height) {
			this.sprite = new game.Graphics();
			this.sprite.fillColor = '#895103';
			this.sprite.drawRect(0, 0, width, height);
			this.sprite.position.set(this.sprite.width / 2 + x, this.sprite.height / 2 + y);
			this.sprite.anchorCenter();
			this.sprite.addTo(game.scene.container);
			this.body = new game.Body({
				mass: 0,
				fixedRotation: false,
				position: [
					this.sprite.position.x / game.scene.world.ratio,
					this.sprite.position.y / game.scene.world.ratio
				],
			});
			var ratio = game.scene.world.ratio;
			this.shape = new p2.Box({
				width: this.sprite.width / ratio,
				height: this.sprite.height / ratio
			});
			this.shape.collisionGroup = game.scene.groups.GROUND;
			this.shape.collisionMask = game.scene.groups.PlayerGroup | game.scene.groups.PlayerGroupFilter | game.scene.groups.GROUND;
			this.body.addShape(this.shape);
			this.body.collisionGroup = game.Body.WallBody;
			this.body.addTo(game.scene.world);
		},
		update: function() {
			this.sprite.position.x = this.body.position[0] * game.scene.world.ratio;
			this.sprite.position.y = this.body.position[1] * game.scene.world.ratio;
			this.sprite.rotation = this.body.angle;
		}
	});
	game.createClass('Solid_Platform', {
		init: function(x, y, width, height) {
			this.sprite = new game.Graphics();
			this.sprite.fillColor = '#2a2a2a';
			this.sprite.drawRect(0, 0, width, height);
			this.sprite.position.set(this.sprite.width / 2 + x, this.sprite.height / 2 + y);
			this.sprite.anchorCenter();
			this.sprite.addTo(game.scene.container);
			this.body = new game.Body({
				mass: 0,
				fixedRotation: false,
				position: [
					this.sprite.position.x / game.scene.world.ratio,
					this.sprite.position.y / game.scene.world.ratio
				],
			});
			var ratio = game.scene.world.ratio;
			this.shape = new p2.Box({
				width: this.sprite.width / ratio,
				height: this.sprite.height / ratio
			});
			this.shape.collisionGroup = game.scene.groups.GROUND;
			this.shape.collisionMask = game.scene.groups.PlayerGroup | game.scene.groups.PlayerGroupFilter | game.scene.groups.GROUND;
			this.body.addShape(this.shape);
			this.body.collisionGroup = game.Body.PlatformBody;
			this.body.addTo(game.scene.world);
		},
		update: function() {
			this.sprite.position.x = this.body.position[0] * game.scene.world.ratio;
			this.sprite.position.y = this.body.position[1] * game.scene.world.ratio;
			this.sprite.rotation = this.body.angle;
		}
	});
	game.createClass('JumpThru_Platform', {
		init: function(x, y, width, height) {
			this.sprite = new game.Graphics();
			this.sprite.fillColor = 'blue';
			this.sprite.drawRect(0, 0, width, height);
			this.sprite.position.set(this.sprite.width / 2 + x, this.sprite.height / 2 + y);
			this.sprite.anchorCenter();
			this.sprite.addTo(game.scene.container);
			this.body = new game.Body({
				mass: 0,
				fixedRotation: false,
				position: [
					this.sprite.position.x / game.scene.world.ratio,
					this.sprite.position.y / game.scene.world.ratio
				],
			});
			var ratio = game.scene.world.ratio;
			this.shape = new p2.Box({
				width: this.sprite.width / ratio,
				height: this.sprite.height / ratio
			});
			this.shape.collisionGroup = game.scene.groups.GROUND;
			this.body.addShape(this.shape);
			this.body.collisionGroup = game.Body.PlatformBody;
			this.body.addTo(game.scene.world);
		},
		update: function() {
			this.sprite.position.x = this.body.position[0] * game.scene.world.ratio;
			this.sprite.position.y = this.body.position[1] * game.scene.world.ratio;
			this.sprite.rotation = this.body.angle;
		}
	});
	game.createClass('Slope_Solid_Platform', {
		init: function(x, y, width, height, angle) {
			this.sprite = new game.Graphics();
			this.sprite.fillColor = '#860000';
			this.sprite.drawRect(0, 0, width, height);
			this.sprite.position.set(this.sprite.width / 2 + x, this.sprite.height / 2 + y);
			this.sprite.anchorCenter();
			this.sprite.addTo(game.scene.container);
			this.body = new game.Body({
				mass: 0,
				angle: angle,
				fixedRotation: false,
				position: [
					this.sprite.position.x / game.scene.world.ratio,
					this.sprite.position.y / game.scene.world.ratio
				],
			});
			var ratio = game.scene.world.ratio;
			this.shape = new p2.Box({
				width: this.sprite.width / ratio,
				height: this.sprite.height / ratio
			});
			this.shape.collisionGroup = game.scene.groups.GROUND;
			this.shape.collisionMask = game.scene.groups.PlayerGroup | game.scene.groups.PlayerGroupFilter | game.scene.groups.GROUND;
			this.body.addShape(this.shape);
			this.body.collisionGroup = game.Body.SlopePlatformBody;
			this.body.addTo(game.scene.world);
		},
		update: function() {
			this.sprite.position.x = this.body.position[0] * game.scene.world.ratio;
			this.sprite.position.y = this.body.position[1] * game.scene.world.ratio;
			this.sprite.rotation = this.body.angle;
		}
	});
	game.createClass('MovingSolid_Platformer_Horizon', {
		init: function(x, y, width, height, hx, speed) {
			this.speed = speed;
			this.sprite = new game.Graphics();
			this.sprite.fillColor = '#2a2a2a';
			this.sprite.drawRect(0, 0, width, height);
			this.sprite.position.set(this.sprite.width / 2 + x, this.sprite.height / 2 + y);
			this.sprite.anchorCenter();
			this.sprite.addTo(game.scene.container);
			this.body = new game.Body({
				mass: 0,
				fixedRotation: false,
				position: [
					this.sprite.position.x / game.scene.world.ratio,
					this.sprite.position.y / game.scene.world.ratio
				],
			});
			var ratio = game.scene.world.ratio;
			this.shape = new p2.Box({
				width: this.sprite.width / ratio,
				height: this.sprite.height / ratio
			});
			this.shape.collisionGroup = game.scene.groups.GROUND;
			this.shape.collisionMask = game.scene.groups.PlayerGroup | game.scene.groups.PlayerGroupFilter | game.scene.groups.GROUND;
			this.body.addShape(this.shape);
			this.body.collisionGroup = game.Body.Float_PLATFORM_HORIZON;
			this.body.collideAgainst = [game.Body.PLAYER];
			this.body.addTo(game.scene.world);
			game.Tween.add(this.sprite.position, {
				x: hx
			}, speed, {
				repeat: Infinity,
				yoyo: true
			}).start();
		},
		collidePlayer: function() {
			for (var i = 0; i < game.scene.world.narrowphase.contactEquations.length; i++) {
				var c = game.scene.world.narrowphase.contactEquations[i];
				if ((c.bodyA === this.body && c.bodyB.collisionGroup === game.Body.PlayerBody) || (c.bodyB === this.body && c.bodyA.collisionGroup === game.Body.PlayerBody)) {
					return true;
				}
			}
			return false;
		},
		update: function() {
			if (this.collidePlayer()) {
				game.scene.player.body.position[0] -= (this.body.position[0] - (this.sprite.position.x / game.scene.world.ratio));
			}
			this.body.position[0] = this.sprite.position.x / game.scene.world.ratio;
			this.body.position[1] = this.sprite.position.y / game.scene.world.ratio;
			this.sprite.rotation = this.body.angle;
		}
	});
	game.createClass('MovingSolid_Platformer_Vertical', {
		init: function(x, y, width, height, vy, speed) {
			this.speed = speed;
			this.sprite = new game.Graphics();
			this.sprite.fillColor = '#2a2a2a';
			this.sprite.drawRect(0, 0, width, height);
			this.sprite.position.set(this.sprite.width / 2 + x, this.sprite.height / 2 + y);
			this.sprite.anchorCenter();
			this.sprite.addTo(game.scene.container);
			this.body = new game.Body({
				mass: 0,
				fixedRotation: false,
				position: [
					this.sprite.position.x / game.scene.world.ratio,
					this.sprite.position.y / game.scene.world.ratio
				],
			});
			var ratio = game.scene.world.ratio;
			this.shape = new p2.Box({
				width: this.sprite.width / ratio,
				height: this.sprite.height / ratio
			});
			this.shape.collisionGroup = game.scene.groups.GROUND;
			this.shape.collisionMask = game.scene.groups.PlayerGroup | game.scene.groups.PlayerGroupFilter | game.scene.groups.GROUND;
			this.body.addShape(this.shape);
			this.body.collisionGroup = game.Body.Float_PLATFORM_VERTICAL;
			this.body.collideAgainst = [game.Body.PLAYER];
			this.body.addTo(game.scene.world);
			game.Tween.add(this.sprite.position, {
				y: vy
			}, speed, {
				repeat: Infinity,
				yoyo: true
			}).start();
		},
		collidePlayer: function() {
			for (var i = 0; i < game.scene.world.narrowphase.contactEquations.length; i++) {
				var c = game.scene.world.narrowphase.contactEquations[i];
				if ((c.bodyA === this.body && c.bodyB.collisionGroup === game.Body.PlayerBody) || (c.bodyB === this.body && c.bodyA.collisionGroup === game.Body.PlayerBody)) {
					return true;
				}
			}
			return false;
		},
		update: function() {
			if (this.collidePlayer()) {
				game.scene.player.body.position[1] -= (this.body.position[1] - (this.sprite.position.y / game.scene.world.ratio));
			}
			this.body.position[0] = this.sprite.position.x / game.scene.world.ratio;
			this.body.position[1] = this.sprite.position.y / game.scene.world.ratio;
			this.sprite.rotation = this.body.angle;
		}
	});
	game.createClass('Dialogue', {
		init: function(x, y, txt) {
			this.text = new game.SystemText(txt);
			this.text.size = 18;
			this.text.color = '#fdfdfd';
			this.text.position.set(x, y);
			this.text.addTo(game.scene.container);
		}
	});
	// Attributes for different body types
	game.addAttributes('Body', {
		PlayerBody: 0,
		PlayerAttackBody: 1,
		PlatformBody: 2,
		SlopePlatformBody: 3,
		WallBody: 4
	});
});

 

Share this post


Link to post
Share on other sites

Niice to see! Played the demo, and I'll check the code out later.

One thing is the brown wall jumping(vertical jumping) seems a bit glitchy? The player seems to blip away from the wall, rather than move smoothly.

15 hours ago, khleug35 said:

P2 Plugin is not open source, so the download file is not include p2.js.

Yeah, I wanted to share a few demo too, but is a bit awkward if we can't include the plugin.

I wonder if it's a good idea to have some kind of build option in the Panda Editor. I.e. so in the Editor, you can say 'include P2.js plugin'. The editor will check: If you have it locally, great, it'll use it. Otherwise, it will try to download it for you from the Panda website.

This way, we can share our open-source projects that use plugins a bit easier? cc: @enpu 

 

Share this post


Link to post
Share on other sites
On 8/27/2019 at 9:00 AM, Wolfsbane said:

I wonder if it's a good idea to have some kind of build option in the Panda Editor. I.e. so in the Editor, you can say 'include P2.js plugin'.

You can export to web from the editor, this will create one JS file which includes everything. You can use that to share your demos.

Share this post


Link to post
Share on other sites
7 hours ago, enpu said:

You can export to web from the editor, this will create one JS file which includes everything. You can use that to share your demos.

I don't mean for the demo export, I mean for the project source code.

E.g. khleug has uploaded the project to GitHub. If I want to play with the code (or collaborate) I have to checkout, manually eyeball the requires() section, and then hunt down the right plugins I need.

So if Panda editor was smart enough to read the requires for us, and then pull the plugin into the workspace that would save time, and make it easier. (Maybe? I'm just thinking out loud)

I'm a Java monkey, so I'm thinking along the lines of a Maven build file. 

Share this post


Link to post
Share on other sites

@Wolfsbane

Thank you for playing and comment, I fixed the brown wall jumping problem, It may be more smoothly now.

I agree with your suggestion. Panda editor can trial the plugin.

@enpu

Yes, This demo is export to web and uploads to itch io.

Hope Panda2 have best future:lol: waiting for the Multiplayer plugin,thanks

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.