小言_互联网的博客

游戏制作-洛克人游戏html5版(可以在线预览)

296人阅读  评论(0)

介绍

最近用 Html 制作了一款洛克人游戏,直接上效果图:

预览地址

效果链接(只看看不如亲自体验一下):

http://h5demo.yyfuncdn.com/res/gameDemo/man/

手机扫描运行:

最近用 pixi 和 js 制作了一款洛克人游戏,致敬一下经典,简单实现了人物的跑动、跳跃、以及镜头跟随的效果,在手机及网页做了相关适配,下面是手机横屏的一些操作效果图:

接下来我们来看一看源码,是如何实现这些功能的

先给大家分享到这里~更多的可以下载源码看一看,以下是源码下载链接:
点击下载
(http://h5demo.yyfuncdn.com/res/gameDemo/man.zip)

这是所有的项目文件

index 页面为这个项目的运行文件,主要用于创建舞台以及适配,利用 Game 对象来创建整个游戏场景

完整代码

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<title>Demo</title>
	<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
	<script src="js/pixi.min.js"></script>
	<script src="js/game.js"></script>
	<script src="js/people.js"></script>
	<script src="js/ground.js"></script>
	<script src="js/button.js"></script>
	<script src="js/Animation.js"></script>
    <script src="js/AnimationData.js"></script>
	<style>
		body{
    
			margin: 0;
		}
	</style>
</head>
<body>
	<script>
		//判断横竖屏及刷新
		function orientationChange() {
    
		    switch(window.orientation) {
    
		      case 0:   //竖屏
					alert("建议在横屏状态下浏览");
					window.localStorage.setItem('name','a');
		            break;
		      case -90: //横屏
							var shu=window.localStorage.getItem('name');
							console.log(shu)
							if(shu=='a'){
    
									window.location.reload();
									window.localStorage.setItem('name','b');
							}
							//alert('竖屏')
							orientation = 'portrait';
		            break;
		      case 90:   //横屏
							var shu=window.localStorage.getItem('name')
							if(shu=='a'){
    
									window.location.reload();
									window.localStorage.setItem('name','b');
							}
							//alert('竖屏')
							orientation = 'portrait';
		            break;
		      case 180:   //竖屏
					alert("建议在横屏状态下浏览");
					window.localStorage.setItem('name','a');
		          break;
		    };

		};
		window.addEventListener("load", orientationChange, false);
    	window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", orientationChange, false);
		
		//获取当前屏幕宽高
		var html = document.getElementsByTagName('html')[0];
		var phoneHeight = html.clientHeight;
		var phoneWidth = html.clientWidth;
		//创建当前屏幕大小舞台
		var app = new PIXI.Application(phoneWidth,phoneHeight);
		document.body.appendChild(app.view);
		//创建游戏界面
		var game = new Game();
		app.stage.addChild(game.gameCeng);

		//操作提示
		var text = new PIXI.Text("键盘操作:A 左移动 D 右移动 K 跳跃");
        text.x = 50;
        text.y = 50;
        text.style.fill = 0xffffff; 
        app.stage.addChild(text);
    
		//创建帧频动画
		app.ticker.add(animate);
		var frame = 1;
		function animate() {
    
			if(frame >= Math.round(app.ticker.FPS / 60)) {
    
				game.update();
				frame = 0;
			}
			frame ++;
		}
	</script>
</body>
</html>

接下来再创建整个游戏场景,即 Game 对象,里面创建图层用于码放所有的背景、角色、按钮,并且在最后还添加了键盘事件用于操控角色

function Game(){
   
    //当前屏幕宽高比
    var screenScale = phoneHeight / phoneWidth;

    //自适应缩放比例
    var scale = (phoneHeight / 561);

    //游戏元素图层
    this.gameCeng = new PIXI.Container();
    //游戏元素图层场景图层
    this.gameCjCeng = new PIXI.Container();
    this.gameCeng.addChild(this.gameCjCeng);

    //游戏元素人物场景图层
    this.gameManCeng = new PIXI.Container();
    this.gameCeng.addChild(this.gameManCeng);

    //背景
    var background = new PIXI.Sprite.fromImage("img/bg.png");
    this.gameCjCeng.addChild(background);
    background.width = phoneWidth;
    background.height = phoneHeight;

    //背景图宽高比
    var bgScale = 640 / 960;
    if(bgScale > screenScale) {
   
        background.width = phoneWidth;
        background.height = 640 * (phoneWidth / 960);
    } else {
   
        background.width = 960 * (phoneHeight / 640);
        background.height = phoneHeight;
    }

    //路面
    var ground = new Ground();
    this.gameCjCeng.addChild(ground.groundView);


    //人物
    var people = new People();
    ground.groundView.addChild(people.peopleView);
    people.player.init(action_data);
    people.player.play("stand")

    //向左按钮
    var leftBtn = new Button("img/left.png",0,phoneHeight*0.2,phoneHeight - 150*scale,people);
    this.gameManCeng.addChild(leftBtn.buttonView);

    //向右按钮
    var rightBtn = new Button("img/right.png",1,phoneHeight*0.2+150*scale,phoneHeight - 150*scale,people);
    this.gameManCeng.addChild(rightBtn.buttonView);

    //跳跃按钮
    var jumpBtn = new Button("img/jump.png",2,phoneWidth - 200*scale,phoneHeight - 150*scale,people);
    this.gameManCeng.addChild(jumpBtn.buttonView);
    
    this.update = function() {
   
        //人物移动
        people.update();
        //地面跟随
        ground.groundFollow(people.peopleView);
    }

    //键盘控制
    document.onkeydown = function (e) {
   
        var e = e || window.event;  //标准化事件对象
        var keyCode = e.keyCode;

        if(keyCode == 75) {
    //k 跳跃
            people.textureType = 2;
            people.isJump = true;
        } else if (keyCode == 65) {
    //a 左
            if(people.textureType != 2) {
   
                people.textureType = 1;
            }
            people.peopleView.scale.x = -1;
            people.isMove = true;
            people.speedX = -(phoneWidth / 180);
        } else if (keyCode == 68) {
    //d 右
            if(people.textureType != 2) {
   
                people.textureType = 1;
            }
            people.peopleView.scale.x = 1;
            people.isMove = true;
            people.speedX = phoneWidth / 180;
        }
    }

    document.onkeyup = function (e) {
   
        var e = e || window.event;  //标准化事件对象
        var keyCode = e.keyCode;

        if(keyCode == 65 || keyCode == 68) {
    //a 左 d 右
            people.isMove = false;
            people.speedX = 0;
            if(people.textuerType != 2 && people.peopleView.y == -phoneHeight*0.3/scale) {
   //判断人物是否在地面
                people.player.play("stand");
                people.textureType = 3;
            }
        }
    }
}

整个人物对象我封装到了 People 对象中,并写了他的移动、跳跃的方法,人物动画的纹理切换我封装成了动画工具 Animation 对象,即存储好所有的纹理图及播放间隔时间即可

function People(){
   
    var self = this;
    var scale = phoneHeight / 561;//自适应缩放比例
    this.textureType = 3;//人物运动类型:1 为跑动 2 为跳跃 3 为静止
    this.player = new Animation();//人物动画
    this.peopleView = this.player.sprite;//人物图片
    this.peopleView.x = phoneWidth*0.3/scale;//人物 x 坐标 & 在所有屏幕位置自适应
    this.peopleView.y = -phoneHeight*0.3/scale;//人物 y 坐标 & 在所有屏幕位置自适应
    this.peopleView.scale.x = 1;//人物图片翻转
    this.peopleView.anchor.set(0.6,1);//人物图片锚点设置
    this.jumpSpeed = -7;//跳跃距离&跳跃初始速度
    this.speedX = 0;//水平速度
    this.speedY = this.jumpSpeed;//垂直速度
    this.isMove = false;//人物移动开关量
    this.isJump = false;//人物跳跃开关量
    this.distance = 0;//移动距离

    this.peopleMove = function() {
   //人物移动
        if(this.isMove) {
   
            //人物跑动动画
            if(this.textureType == 1) {
   
                self.player.play("run");
            }
            //控制人物移动
            if(this.peopleView.x <= 0 && this.speedX < 0) {
    // 判断左极限
                this.peopleView.x = 0;
            } else if (this.peopleView.x >= 2300 && this.speedX > 0) {
    // 判断右极限
                this.peopleView.x = 2300;
            } else {
   
                this.peopleView.x += this.speedX;
            }
        }
    }

    this.peopleJump = function() {
   //人物跳跃
        if(this.isJump) {
   
            //人物跳跃动画
            this.speedY += 0.2;
            this.peopleView.y += this.speedY;
            if(this.speedY < 0){
   
                this.player.play("jump");
            }else{
   
                this.player.play("fall");
            }
            if(this.peopleView.y >= -phoneHeight*0.3/scale) {
   //结束跳跃
                this.peopleView.y = -phoneHeight*0.3/scale;
                this.isJump = false;
                this.speedY = this.jumpSpeed;
                if(self.isMove) {
   
                    this.textureType = 1;
                    this.player.play("run");
                } else {
   
                    this.textureType = 3;
                    this.player.play("fallover");
                    this.player.play("stand");
                }
            }
        }
        
    }

    this.update = function(){
   
        this.peopleMove();
        this.peopleJump();
        this.player.action();
    }
}

所有的按钮都是共用一个 Button 对象创建,每次传入对应数据即可,其方法根据数据的不同绑定不同的方法

function Button(url,type,x,y,object) {
   //路径 类型 x坐标 y坐标 控制对象
    var self = this;
    var scale = phoneHeight / 561;
    this.url = url;
    this.type = type;// 0 :为向左按钮 1 :为向右按钮 2:为跳跃按钮
    this.buttonView = new PIXI.Sprite.fromImage(this.url);
    this.buttonView.x = x;
    this.buttonView.y = y;
    this.buttonView.width = 100*scale;
    this.buttonView.height = 100*scale;
    this.buttonView.interactive = true;
    this.speedX = phoneWidth / 180;
    this.buttonView.on("pointerdown",function() {
   //按下
        if(self.type == 0) {
   //左按钮
            if(object.textureType != 2) {
   
                object.textureType = 1;
            }
            object.peopleView.scale.x = -1;
            object.isMove = true;
            object.speedX = -self.speedX;
        } else if (self.type == 1) {
   //右按钮
            if(object.textureType != 2) {
   
                object.textureType = 1;
            }
            object.peopleView.scale.x = 1;
            object.isMove = true;
            object.speedX = self.speedX;
        } else if (self.type == 2) {
   //跳跃按钮
            object.textureType = 2;
            object.isJump = true;
        }
    })
    this.buttonView.on("pointerup",function() {
   //抬起
        if(self.type == 0 || self.type == 1) {
   //左按钮 & 右按钮
            object.isMove = false;
            object.speedX = 0;
            if(object.textuerType != 2 && object.peopleView.y == -phoneHeight*0.3/scale) {
   //判断人物是否在地面
                object.player.play("stand");
                object.textureType = 3;
            }
        }
    })
}

角色跟随这里是我最满意的地方,为了体验的舒适并没有做成同步移动,而是跟随的效果,这里是利用的角色绑定在地面图片上,保证角色始终保持在中间位置,当脱离中间位置,地面跟随移动,保证其位置始终在中间

function Ground() {
   
    var self = this;
    var scale = phoneHeight / 561;//自适应缩放比例
    this.groundView = new PIXI.Sprite.fromImage("img/ground.png");
    this.groundView.anchor.set(0,1);
    this.groundView.y = phoneHeight+phoneHeight*0.2;
    this.groundView.height = phoneHeight;
    this.groundView.width = 2300 * scale;
    this.speedX = -5;
    this.distance = 0;
    this.groundFollow = function(obj) {
   
        var obj = obj.getGlobalPosition();//获取角色当前窗口位置
        if(obj.x != phoneWidth/2) {
   //判断角色位置偏移窗口中心点
            var deviation_x = obj.x-phoneWidth/2;//计算角色X值与窗口中心点偏移距离
            var correct_speed = deviation_x/10;//计算角色矫正速度
            self.groundView.x -= correct_speed;
            if(self.groundView.x >= 0) {
   //判断场景为最左侧时矫正场景位置
                self.groundView.x = 0;
                correct_speed = 0;
            } else if (self.groundView.x <= phoneWidth-self.groundView.width) {
   //判断场景为最右侧时矫正场景位置
                self.groundView.x = phoneWidth-self.groundView.width;
                correct_speed = 0;
            }
        }
        if(obj.y != phoneHeight*0.9) {
   //判断角色位置偏移窗口中心点
            var deviation_y = obj.y-phoneHeight*0.9;//计算角色Y值与窗口中心点偏移距离
            var correct_speed = deviation_y/10;//计算角色矫正速度
            self.groundView.y -= correct_speed;
            if(self.groundView.y >= phoneHeight+phoneHeight*0.4) {
   //判断场景为最上侧时矫正场景位置
                self.groundView.y = phoneHeight+phoneHeight*0.4;
                correct_speed = 0;
            } else if (self.groundView.y <= phoneHeight+phoneHeight*0.2) {
   //判断场景为最下侧时矫正场景位置
                self.groundView.y = phoneHeight+phoneHeight*0.2;
                correct_speed = 0;
            }
        }
    }
}

下面的即是我动画方法的封装,类似相关的动画都可以用其实现,非常的通用

function Animation() {
   

    var self = this;

    this.sprite = new PIXI.Sprite();
    this.frameData = {
   };

    this.frameIndex = 0;
    this.actionName = "";

    this.waitTime = 0;

    this.isPlay = true; //是否播放状态
    this.targetFrame = -1; //目标帧

    this.init = function(frameData) {
   
        this.frameData = frameData;
    }

    this.play = function(actionName) {
   

        if(self.actionName != actionName) {
   
            self.actionName = actionName;
            self.frameIndex = 0;
            self.waitTime = 0;
            self.action(true);
        } 
        
    }

    this.update = function() {
   
        
        this.action();
    
    }

    this.action = function(isUpdate = false) {
   
        var frameArr = self.frameData[self.actionName];
        if(self.frameIndex == frameArr.length) {
   
            self.frameIndex = 0;
        }

        //切换纹理
        var frameInfo = frameArr[self.frameIndex];
        if(frameInfo.wait == self.waitTime || isUpdate == true) {
   
            this.sprite.texture = new PIXI.Texture.fromImage(frameInfo.url);
            this.frameIndex ++;
            self.waitTime = 0;

        } else {
   
            self.waitTime ++;
        }
    }

}

人物动画数据


var action_data = {
   
    "stand": [
        {
   "url":"img/Z0001.png", "wait": 20, "frame": 1},
        {
   "url":"img/Z0002.png", "wait": 15, "frame": 2},
        {
   "url":"img/Z0003.png", "wait": 15, "frame": 3},
        {
   "url":"img/Z0004.png", "wait": 15, "frame": 4},
        {
   "url":"img/Z0005.png", "wait": 15, "frame": 5},
        {
   "url":"img/Z0006.png", "wait": 15, "frame": 6},
    ],
    "run": [
        {
   "url":"img/P0001.png", "wait": 5, "frame": 1},
        {
   "url":"img/P0002.png", "wait": 5, "frame": 2},
        {
   "url":"img/P0003.png", "wait": 5, "frame": 3},
        {
   "url":"img/P0004.png", "wait": 5, "frame": 4},
        {
   "url":"img/P0005.png", "wait": 5, "frame": 5},
        {
   "url":"img/P0006.png", "wait": 5, "frame": 6},
        {
   "url":"img/P0007.png", "wait": 5, "frame": 7},
        {
   "url":"img/P0008.png", "wait": 5, "frame": 8},
        {
   "url":"img/P0009.png", "wait": 5, "frame": 9},
        {
   "url":"img/P0010.png", "wait": 5, "frame": 10},
    ],
    "jump": [
        {
   "url":"img/T0001.png", "wait": 5, "frame": 1},
        {
   "url":"img/T0002.png", "wait": 5, "frame": 2},
        {
   "url":"img/T0003.png", "wait": 5, "frame": 3},
        {
   "url":"img/T0004.png", "wait": 5, "frame": 4},
        {
   "url":"img/T0005.png", "wait": 5, "frame": 5},
        {
   "url":"img/T0006.png", "wait": 5, "frame": 6},
        {
   "url":"img/T0007.png", "wait": 5, "frame": 7},
        {
   "url":"img/T0008.png", "wait": 5, "frame": 8},
        {
   "url":"img/T0009.png", "wait": 5, "frame": 9},
        {
   "url":"img/T0010.png", "wait": 120, "frame": 10},
    ],
    "fall": [
        {
   "url":"img/T0007.png", "wait": 5, "frame": 7},
        {
   "url":"img/T0008.png", "wait": 5, "frame": 8},
        {
   "url":"img/T0009.png", "wait": 5, "frame": 9},
        {
   "url":"img/T0010.png", "wait": 5, "frame": 10},
    ],
    "fallover": [
        {
   "url":"img/T0011.png", "wait": 5, "frame": 9},
        {
   "url":"img/T0012.png", "wait": 5, "frame": 10},
    ]

}

转载:https://blog.csdn.net/dayouyu001/article/details/115489498
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场