这是一款横版跑酷类游戏,应为是JavaScript的所以不需要其他复杂的配置和环境,点击就能运行
线上试玩:http://longsong.games/newmoon
文末有代码地址
操作介绍:
↑ 跳跃 ↓ 坐下 跳砍 ← 往后跑 → 向前跑 enter 无敌翻滚 d 显示外形 q 重新开始游戏 energy 能量
下面是项目的目录:
其中主要的几个:
main.js:
import { Player } from './player.js';
import { InputHandle } from './input.js';
import { Background} from './background.js';
import { FlyingEnemy, ClimbEnemy, GroundEnemy} from './enemies.js';
import { UI } from './UI.js';
window.addEventListener('load', function(){
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = 900;
canvas.height = 500;
let enemies = [];
let score = 0;
class Game{
constructor(width, height){
this.width = width;
this.height = height;
this.groundMargin = 40;
this.speed = 0;
this.maxSpeed = 5;
this.background = new Background(this);
this.player = new Player(this);
this.input = new InputHandle(this);
this.UI = new UI(this);
this.enemies = [];
this.particles = [];
this.collisions = [];
this.floatingMessages = [];
this.maxParticles = 200;
this.enemyTimer = 0;
this.enemyInterval = 1000;
this.debug = true;
this.score = 0;
this.energy = 0;
this.time = 0;
this.winningScore = 40;
this.maxTime = 300000;
this.fontColor = 'black';
this.player.currentState = this.player.state[0];
this.player.currentState.enter();
this.lives = 5;
this.game = false;
this.pan = 1;
}
update(deltaTime){
this.time += deltaTime;
if (this.time > this.maxTime) this.gameOver = true;
if (this.energy >= 0 ) this.energy += 1;
if (this.energy < 0 && this.pan == 1 )this.energy += 1;
this.background.update();
this.player.update(this.input.keys, deltaTime);
if(this.enemyTimer > this.enemyInterval){
this.addEnemy();
this.enemyTimer =0;
}
else {
this.enemyTimer += deltaTime;
}
//handleEnemies
this.enemies.forEach(enemy =>{
enemy.update(deltaTime);
})
//handle message
this.floatingMessages.forEach(message =>{
message.update(deltaTime);
})
//handle partyicles
this.particles.forEach((particle, index) =>{
particle.update();
});
if (this.particles.length > this.maxParticles) {
this.particles.length = this.maxParticles;
}
this.collisions.forEach((collision, index) => {
collision.update(deltaTime);
});
this.collisions = this.collisions.filter(collision => !collision.markedForDeletion);
this.particles = this.particles.filter(particle => !particle.markedForDeletion);
this.enemies = this.enemies.filter(enemy => !enemy.markedForDeletion);
this.floatingMessages = this.floatingMessages.filter(message => !message.markedForDeletion);
}
draw(context){
this.background.draw(context);
this.player.draw(context);
this.enemies.forEach(enemy =>{
enemy.draw(context);
});
this.particles.forEach(particle => {
particle.draw(context);
});
this.collisions.forEach(collision => {
collision.draw(context);
this.floatingMessages.forEach(message =>{
message.draw(context);
})
});
this.UI.draw(context);
}
addEnemy(){
if (this.speed > 0 && Math.random() < 0.5) this.enemies.push(new GroundEnemy(this));
else if (this.speed > 0 ) this.enemies.push(new ClimbEnemy(this));
this.enemies.push(new FlyingEnemy(this));
console.log(this.enemies);
}
restartGame(){
this.player.restart();
this.background.restart();
this.enemies = [];
this.gameOver = false;
this.score = 0;
this.energy = 0;
this.lives = 5;
this.time = 0;
animate(0);
}
}
const game = new Game(canvas.width, canvas.height);
let lastTime = 0;
function animate(timeStamp){
const deltaTime = timeStamp - lastTime;
lastTime = timeStamp;
ctx.clearRect(0, 0, canvas.width, canvas.height);
game.update(deltaTime);
game.draw(ctx);
if (!game.gameOver) requestAnimationFrame(animate);
}
animate(0);
})
player.js
import { Sitting, Runing, Jumping, Falling, Rolling, Diving, Hit} from './playerStates.js';
import { CollisionAnimation } from './CollisionAnimation.js'
import { FloatingMessage } from './floatingMessages.js'
export class Player{
constructor(game){
this.game = game;
this.width = 100;
this.height = 91.3;
this.x = 0;
this.y = this.game.height - this.height - this.game.groundMargin;
this.vy = 0;
this.weight = 1;
this.image = document.getElementById('player');
this.frameX = 0; //第几行图片
this.frameY = 0; //第几个状态
this.maxFrame;
this.fps = 20; //帧数
this.frameInterval = 500/this.fps;
this.f = 0;
this.speed = 0;
this.maxSpeed = 10;
this.state = [new Sitting(this.game), new Runing(this.game), new Jumping(this.game), new Falling(this.game), new Rolling(this.game), new Diving(this.game), new Hit(this.game)];
this.currentState = null;
}
update(input, deltaTime){
//this.x++;
//horizon movement
this.checkCollision();
this.currentState.handleInput(input);
this.x += this.speed;
if (input.includes('ArrowRight') && this.currentState !== this.state[6]) this.speed = this.maxSpeed;
else if (input.includes('ArrowLeft') && this.currentState !== this.state[6]) this.speed = -this.maxSpeed;
else this.speed = 0;
if (this.currentState === this.state[4]) this.game.energy -= 4;
if (this.x <0) this.x = 0;
if (this.x > this.game.width - this.width) this.x = this.game.width - this.width;
//vertical movement
this.y += this.vy;
if (!this.onGround()) this.vy += this.weight;
else this.vy = 0;
//console.log(this.game.height - this.height);
// sprite animation
if (this.y > this.game.height - this.height - this.game.groundMargin) this.y = this.game.height - this.height - this.game.groundMargin;
if (this.f > this.frameInterval){
this.f = 0;
if (this.frameX < this.maxFrame) this.frameX++;
else this.frameX = 0;
}
else {
this.f =this.f + deltaTime;
}
//if (this.frameX < this.maxFrame) this.frameX++;
//else this.frameX = 0;
}
restart(){
this.x = 0;
this.y = this.game.height - this.height - this.game.groundMargin;
}
draw(context){
if (this.game.debug) context.strokeRect(this.x, this.y ,this.width, this.height);
context.drawImage(this.image, this.frameX * this.width, this.frameY * this.height, this.width, this.height,this.x,this.y, this.width, this.height);
}
onGround(){
return this.y >= this.game.height - this.height - this.game.groundMargin;
}
setState(state, speed){
this.currentState = this.state[state];
this.game.speed = this.game.maxSpeed * speed;
this.currentState.enter();
}
checkCollision(){
this.game.enemies.forEach(enemy => {
if (enemy.x < this.x + this.width &&
enemy.x + enemy.width > this.x &&
enemy.y < this.y + this.height &&
enemy.y + enemy.height > this.y
){
enemy.markedForDeletion = true;
this.game.collisions.push(new CollisionAnimation(this.game, enemy.x + enemy.width * 0.5,enemy.y + enemy.height * 0.5 ));
if (this.currentState === this.state[4] || this.currentState ===this.state[5]){
this.game.score++;
this.game.energy += 100;
this.game.floatingMessages.push(new FloatingMessage('+1', enemy.x, enemy.y,150, 50));
}
else {
this.setState(6, 0);
this.game.score -=5 ;
this.game.lives --;
if (this.game.lives <= 0) this.game.gameOver = true;
}
//collosion detected
} else {
//no collision
}
});
}
}
playerState.js
import { Dust,Fire,Splash } from './particles.js'
const state = { //状态
SITTING: 0,
RUNNING: 1,
JUMPING: 2,
FALLING: 3,
ROLLING: 4,
DIVING: 5,
HIT: 6
}
class State {
constructor(state, game){
this.state = state;
this.game = game;
}
}
export class Sitting extends State {
constructor(game){
super('SITTING', game);
}
enter(){
this.game.player.frameX = 0;
this.game.player.frameY = 5;
this.game.player.maxFrame = 4;
}
handleInput(input){
//对初始状态的输入
if (input.includes('ArrowLeft') || input.includes('ArrowRight')){
this.game.player.setState(state.RUNNING, 1);
}
else if (input.includes('ArrowUp')){
this.game.player.setState(state.JUMPING, 1);
}
else if (input.includes('Enter') && this.game.energy >= 0){
this.game.player.setState(state.ROLLING, 2);
}
}
}
export class Runing extends State { //奔跑
constructor(game){
super('RUNNING', game);
}
enter(){
this.game.player.frameX = 0;
this.game.player.frameY = 3;
this.game.player.maxFrame = 8;
}
handleInput(input){
if (input.includes('Enter')) {
this.game.pan = 0;
}
else this.game.pan = 1;
this.game.particles.unshift(new Dust(this.game, this.game.player.x + this.game.player.width * 0.7, this.game.player.y + this.game.player.height));
if (input.includes('ArrowDown')){
this.game.player.setState(state.SITTING, 0);
}
else if (input.includes('ArrowUp')){
this.game.player.setState(state.JUMPING, 1);
}else if (input.includes('Enter')&& this.game.energy >= 0){
this.game.player.setState(state.ROLLING, 2);
}
}
}
export class Jumping extends State { //跳远
constructor(game){
super('JUMPING',game);
}
enter(){
if (this.game.player.onGround()) this.game.player.vy -= 27;
this.game.player.frameX = 0;
this.game.player.frameY = 1;
this.game.player.maxFrame = 6;
}
handleInput(input){
if (input.includes('Enter')) {
this.game.pan = 0;
}
else this.game.pan = 1;
if (this.game.player.vy > this.game.player.weight){
this.game.player.setState(state.FALLING, 1);
}else if (input.includes('Enter') && this.game.energy >= 0){
this.game.player.setState(state.ROLLING, 2);
}else if (input.includes('ArrowDown')){
this.game.player.setState(state.DIVING, 0);
}
}
}
export class Falling extends State { //掉落
constructor(game){
super('FALLING', game);
}
enter(){
this.game.player.frameX = 0;
this.game.player.frameY = 2;
this.game.player.maxFrame = 6;
}
handleInput(input){
if (input.includes('Enter')) {
this.game.pan = 0;
}
else this.game.pan = 1;
if (this.game.player.onGround()){
this.game.player.setState(state.RUNNING, 1);
}else if (input.includes('ArrowDown')){
this.game.player.setState(state.DIVING, 0);
}else if (input.includes('Enter') && this.game.energy >= 0){
this.game.player.setState(state.ROLLING, 2);
}
}
}
export class Rolling extends State { //冲刺
constructor(game){
super('ROLLING', game);
}
enter(){
this.game.player.frameX = 0;
this.game.player.frameY = 6;
this.game.player.maxFrame = 6;
}
handleInput(input){
this.game.particles.unshift(new Fire(this.game, this.game.player.x + this.game.player.width * 0.5, this.game.player.y + this.game.player.height * 0.5));
if (this.game.energy < 0){
if (this.game.player.onGround()){
this.game.player.setState(state.RUNNING, 1);
} else if (!this.game.player.onGround()){
this.game.player.setState(state.FALLING, 1);
} else if ( input.includes('ArrowUp') && this.game.player.onGround()){
this.game.player.vy -= 27;
} else if (input.includes('ArrowDown') && !this.game.player.onGround()) {
this.game.player.vy -= 27;
}
}else{
if (!input.includes('Enter') && this.game.player.onGround()){
this.game.player.setState(state.RUNNING, 1);
} else if (!input.includes('Enter') && !this.game.player.onGround()){
this.game.player.setState(state.FALLING, 1);
} else if (input.includes('Enter') && input.includes('ArrowUp') && this.game.player.onGround()){
this.game.player.vy -= 27;
} else if (input.includes('ArrowDown') && !this.game.player.onGround()) {
this.game.player.vy -= 27;
}
}
}
}
export class Diving extends State { //急速下坠
constructor(game){
super('DIVING', game);
}
enter(){
this.game.player.frameX = 0;
this.game.player.frameY = 6;
this.game.player.maxFrame = 6;
this.game.player.vy = 15;
}
handleInput(input){
if (input.includes('Enter')) {
this.game.pan = 0;
}
else this.game.pan = 1;
this.game.particles.unshift(new Fire(this.game, this.game.player.x + this.game.player.width * 0.5, this.game.player.y + this.game.player.height * 0.5));
if (this.game.player.onGround()){
this.game.player.setState(state.RUNNING, 1);
for(let i = 0; i < 30;i++){
this.game.particles.unshift(new Splash(this.game, this.game.player.x + this.game.player.width * 0.5, this.game.player.y + this.game.player.height * 0.5));
}
} else if (input.includes('Enter') && !this.game.player.onGround()){
this.game.player.setState(state.ROLLING, 2);
}
}
}
export class Hit extends State { //眩晕
constructor(game){
super('HIT', game);
}
enter(){
this.game.player.frameX = 0;
this.game.player.frameY = 4;
this.game.player.maxFrame = 10;
}
handleInput(input){
if (this.game.player.frameX >= 10 && this.game.player.onGround() ){
this.game.player.setState(state.RUNNING, 1);
} else if (this.game.player.frameX >= 10 && !this.game.player.onGround()){
this.game.player.setState(state.FALLING, 1);
}
}
}
代码库:
https://github.com/longsongline/NewMoonDog/https://github.com/longsongline/NewMoonDog/
哪里看不懂的可以在下面留言,或者私信问我
图片我都上传到自己的服务器上上了,所以代码库里没有图片
参考:十个小时教程 ,喜欢的可以自己跟着教程做出一个差不多样子的
https://www.youtube.com/watch?v=GFO_txvwK_c&list=WL&index=12