[]【简单跑酷--JS版】---Lv.3 添加地板

最近太忙了 忘记更新教程了 大家别怪我啊。。
说得不好的地方请及时指正
 
第一节
第二节
 
//=======================  美丽的分割线 =======================


前节教程完成了循环滚动背景的实现,本节我们将运动的地板加上
下图是我们要用到的资源


图片1.png


地板素材是这样的 宽度为960 在游戏里面我目前将它分成32份 也就是由30个小的组成
我们把素材复制到资源目录 如下图


图片2.png


打开LayaSample.js 既然多了一个资源 那就要预加载 上一节有讲到
修改加载的地方


//加载图片
Laya.loader.load(["res/background.png", "res/m_background.png", "res/floor.png"], laya.utils.Handler.create(this, onLoaded), laya.utils.Handler.create(this, onLoading, null, false));


首先同样我们到runGame目录新建一个Floor.js



图片3.png


开始编写代码
这里 我们想一下地板有哪些功能?
1、自身从右到左运动 超出左边边界 就移除存入对象池
2、地板上可以添加东西


 
(function(){

/**
* 地板类
*/
function Floor(){

Floor.__super.call(this);
}

//Floor 是一个显示对象 继承此 Sprite
Laya.class(Floor, "Floor", laya.display.Sprite);

var _proto = Floor.prototype;

_proto.init = function(type){

//创建一个帧循环处理函数
Laya.timer.frameLoop(1, this, this.onLoop);
}
//在地板上面添加物品
_proto.addItem = function(){

}

_proto.onLoop = function(){

}

})();


因为是一个跑酷的游戏 所以 我们之前提到过 这个地板最好有一个类专门负责管理她们
因此 我们再创建一个地板管理类 MapFloor.js



图片4.png


地板管理类 主要干嘛呢
1、增加地板
2、删除地板
3、获取地板

这里我把这个MapFloor当成所有floor的父级显示对象 (其实也有其他方式  这里暂不考虑)
嗯 那我们根据上面的三个功能可以写下如下代码


 
(function () {


/**
* 地板地图类
*
*/
function MapFloor(){


MapFloor.__super.call(this);

this.init();
}
//MapFloor 是一个显示对象 继承此 Sprite
Laya.class(MapFloor,"MapFloor", laya.display.Sprite);

var _proto = MapFloor.prototype;

_proto.init = function(){

//创建一个帧循环处理函数
Laya.timer.frameLoop(1, this, this.onLoop);

}

_proto.onLoop = function(){

}

/**
* 增加地板
*/
_proto.addFloor = function(){

}
/**
* 获取地板
*/
_proto.getFloor = function(){

}
/**
* 删除地板
*/
_proto.delFloor = function(){

}

})();


地板和 地板管理类都搭好了 既然是显示对象 那我们就得添加到舞台
打开RunGame.js


 
(function () {
/**
* 游戏入口
*/
function RunGame(){
RunGame.__super.call(this);
this.init();
}
//RunGame 是一个显示对象 继承此 Sprite
Laya.class(RunGame,"RunGame", laya.display.Sprite);

//定义RunGame的prototype
var _proto = RunGame.prototype;

//初始化
_proto.init = function(){
console.log('RunGame Init');
//添加背景
var bg = new Background();
this.addChild(bg);

//添加地板集合
var mapFloor = new MapFloor();
this.addChild(mapFloor);
}
})();


前期工作都做好了~~ 接下来就是把地板显示到舞台上面
打开Floor.js 我们来给floor设置皮肤


 
(function(){

/**
* 地板类
*/
function Floor(){

//背景贴图纹理
this.bgTexture = null;
//背景
this.bg = null;

Floor.__super.call(this);
}

//Floor 是一个显示对象 继承此 Sprite
Laya.class(Floor, "Floor", laya.display.Sprite);

var _proto = Floor.prototype;

_proto.init = function(){
//如果不开启autoSize 父容器的宽度和高度无法获取
this.autoSize = true;
//初始化的时候将坐标放到屏幕右边
this.x = 852;
//y坐标取一个随机值 为什么是32 因为我们的整个素材是 32 * 20 拼起来的
this.y = 32 * 6 + 32 * parseInt(8 * Math.random());
if(this.bg == null){
//贴图纹理
this.bgTexture = Laya.loader.getRes("res/floor.png");

this.bg = new laya.display.Sprite();
this.bg.graphics.clear();
//将当前的坐标向上移动32 方便后面处理人物的位置
this.bg.y = -32;
this.addChild(this.bg);
}
this.bg.graphics.drawTexture(this.bgTexture, 0, 0, 960, 96);
//创建一个帧循环处理函数
Laya.timer.frameLoop(1, this, this.onLoop);
}
//在地板上面添加物品
_proto.addItem = function(){

}
//获取当前地板上面的所有物品
_proto.getItems = function(){
return ;
}

_proto.onLoop = function(){
//让地板的速度和移动比背景快一点
this.x -= 5 * 1.2;

if((this.x + this.width) < 0){
//判断整个floor是否不在屏幕里面了 如果不在了 移除当前floor
Laya.timer.clear(this, this.onLoop);
this.visible = false;
this.removeSelf();
}
}
})();


地板有了 我们打开MapFloor.js 来添加地板


(function () {


/**
* 地板地图类
*
*/
function MapFloor(){


MapFloor.__super.call(this);

this.init();
}
//MapFloor 是一个显示对象 继承此 Sprite
Laya.class(MapFloor,"MapFloor", laya.display.Sprite);

var _proto = MapFloor.prototype;

_proto.init = function(){

//添加地板
this.addFloor();
//创建一个帧循环处理函数
Laya.timer.frameLoop(1, this, this.onLoop);

}

_proto.onLoop = function(){

}

/**
* 增加地板
*/
_proto.addFloor = function(){
var floor = new Floor();
floor.init();
this.addChild(floor);
}
/**
* 获取地板
*/
_proto.getFloor = function(){

}
/**
* 删除地板
*/
_proto.delFloor = function(){

}

})();


对了~~ 我们这里右新增了两个模块 所以别忘记 上一节我们说到的 要在页面引用响应的JS哦


 
我们刷新页面看看
图片5.png

我们会看到地板从屏幕右边慢慢的跑出来了


~~~ 既然是跑酷 一个地板肯定是不够的~~
我们来增加多一点地板
首先我们想一下 如何增加地板 
1、通过时间间隔来生成
2、通过上一个地板左边移除屏幕外面的后 右边剩余的宽度

我们先简单分析一下这两种方式的利与弊
首先 “通过时间间隔来生成”
我们需要一个时间控制器来判断(例如每隔1秒生成一个)
看起来没有什么问题 但是后期我们游戏的地板宽度是随机的 假设上一个地板随机的时候很短 导致与下一个地板之间的间距过大 这个时候 后期玩家从地板之间跳跃就成为一个问题了 
因此我们考虑第二个方案“通过上一个地板左边移除屏幕外面的后 右边剩余的宽度”
接下来我们会用到自定义事件的方式 让Floor通知它父类MapFloor 什么时候改生成新的地板 什么时候删除屏幕外面的地板


既然如此 我们来写一下代码
 
(function(){

/**
* 地板类
*/
function Floor(){

//背景贴图纹理
this.bgTexture = null;
//最大右边距离
this.maxRight = 0;
this.isOutComplete = false;
//背景
this.bg = null;

Floor.__super.call(this);
}


//事件名称
//超过屏幕一定值出发新的floor事件
Floor.OUT_COMPLETE = "floor_out_complete";
//整个地板都不在屏幕里面事件
Floor.OUT_DIE = "floor_out_die";

//Floor 是一个显示对象 继承此 Sprite
Laya.class(Floor, "Floor", laya.display.Sprite);

var _proto = Floor.prototype;

_proto.init = function(){
this.maxRight = 0;
//如果不开启autoSize 父容器的宽度和高度无法获取
this.autoSize = true;
//初始化的时候将坐标放到屏幕右边
this.x = 852;
//y坐标取一个随机值 为什么是32 因为我们的整个素材是 32 * 20 拼起来的
this.y = 32 * 6 + 32 * parseInt(8 * Math.random());
if(this.bg == null){
//贴图纹理
this.bgTexture = Laya.loader.getRes("res/floor.png");

this.bg = new laya.display.Sprite();
this.bg.graphics.clear();
//将当前的坐标向上移动32 方便后面处理人物的位置
this.bg.y = -32;
this.addChild(this.bg);
}
this.bg.graphics.drawTexture(this.bgTexture, 0, 0, 960, 96);
//计算一下右边还剩下多少 用来判断什么时候生成新的floor
//这里是通过游戏宽度 减去 固定 2个 32的宽度 再随机一个长度 这样 可以让地板时间点的出现 更加随机性
this.maxRight = 852 - 32 * 2 - 32 * parseInt(10 * Math.random());
//创建一个帧循环处理函数
Laya.timer.frameLoop(1, this, this.onLoop);
}
//在地板上面添加物品
_proto.addItem = function(){

}
//获取当前地板上面的所有物品
_proto.getItems = function(){
return ;
}

_proto.onLoop = function(){
//让地板的速度和移动比背景快一点
this.x -= 5 * 1.2;

//判断是否除了边界 如果出了 就通知生成新的floor 这里增加一个变量来判断当前是否已经通知外部了
//因为此处是一个循环的处理
if(!this.isOutComplete && (this.x + this.width) < this.maxRight){
this.isOutComplete = true;
this.event(Floor.OUT_COMPLETE, this);
}else if((this.x + this.width) < 0){
//判断整个floor是否不在屏幕里面了 如果不在了 移除当前floor
Laya.timer.clear(this, this.onLoop);
this.visible = false;
this.event(Floor.OUT_DIE, this);
}
}
})();
再修改一下MapFloor.js
(function () {


/**
* 地板地图类
*
*/
function MapFloor(){
//要移除的地板
this.dieFloorList = ;

MapFloor.__super.call(this);

this.init();
}
//MapFloor 是一个显示对象 继承此 Sprite
Laya.class(MapFloor,"MapFloor", laya.display.Sprite);

var _proto = MapFloor.prototype;

_proto.init = function(){

//添加地板
this.addFloor();
//创建一个帧循环处理函数
Laya.timer.frameLoop(1, this, this.onLoop);

}

_proto.onLoop = function(){
//监听有没有地板要移除
while(this.dieFloorList.lenght > 0){
var floor = this.dieFloorList.shift();
floor.removeSelf();
}
}

/**
* 增加地板
*/
_proto.addFloor = function(){
var floor = new Floor();
floor.init();
floor.once(Floor.OUT_COMPLETE, this, this.getFloor);
floor.once(Floor.OUT_DIE, this, this.delFloor);
this.addChild(floor);
}
/**
* 获取地板
*/
_proto.getFloor = function(floor){
this.addFloor();
}
/**
* 删除地板
*/
_proto.delFloor = function(floor){
this.dieFloorList.push(floor);
}

})();


刷新页面看看效果哈~~ 是不是地板开始动了 而且 还有新的
~~

好像每个地板都一样宽度 我们改善一下代码 在生成地板的时候我们随即一下地板的绘制宽度
打开Floor.js 找到


this.bg.graphics.drawTexture(this.bgTexture, 0, 0, 960, 96);


修改为:


 
 
//随机一个范围值
var _w = 32 * (3 + parseInt(19 * Math.random()));
this.bg.graphics.clear();
//这里用到了 Texture.createFromTexture 就是根据宽度和高度来截取一个图片并且返回一个Texture对象
this.bg.graphics.drawTexture(laya.resource.Texture.createFromTexture(this.bgTexture,0,0,_w,96), 0, 0, _w, 96);
刷新页面试一试
 

图片6.png


嗯 宽度是变短了 但是发现没有 右边好假~~~
怎么办。。。。。。。。。。。。。。


 
//=======================  喝杯水想一想有什么办法 =======================
我们看看原图是什么样子的

图片1.png


右边是好的 ~~ 
。。。。
有办法了~~ 我们把右边 用同样的手法绘制出来 和 上面的背景放在一起 是不是就解决了?
之前有收到 这个背景是 32 * 30
上面我们控制地板的宽度的时候用到了


 


laya.resource.Texture.createFromTexture
我们看下API



图片8.png


这里我们只要最右边的图片 也就是 从 32 *29 的位置开始截取图片 
我们来试一下
最终Floor.js代码如下


 
这里我们只要最右边的图片 也就是 从 32 *29 的位置开始截取图片 
我们来试一下
最终Floor.js代码如下
(function(){

/**
* 地板类
*/
function Floor(){

//背景贴图纹理
this.bgTexture = null;
//最大右边距离
this.maxRight = 0;
//判断是否超过右边最大距离了
this.isOutComplete = false;
//背景
this.bg = null;
//背景右边补丁
this.rightBg = null;

Floor.__super.call(this);
}


//事件名称
//超过屏幕一定值出发新的floor事件
Floor.OUT_COMPLETE = "floor_out_complete";
//整个地板都不在屏幕里面事件
Floor.OUT_DIE = "floor_out_die";

//Floor 是一个显示对象 继承此 Sprite
Laya.class(Floor, "Floor", laya.display.Sprite);

var _proto = Floor.prototype;

_proto.init = function(){
this.maxRight = 0;
//如果不开启autoSize 父容器的宽度和高度无法获取
this.autoSize = true;
//初始化的时候将坐标放到屏幕右边
this.x = 852;
//y坐标取一个随机值 为什么是32 因为我们的整个素材是 32 * 20 拼起来的
this.y = 32 * 6 + 32 * parseInt(8 * Math.random());
if(this.bg == null){
//贴图纹理
this.bgTexture = Laya.loader.getRes("res/floor.png");

this.bg = new laya.display.Sprite();
this.bg.graphics.clear();
//将当前的坐标向上移动32 方便后面处理人物的位置
this.bg.y = -32;
this.addChild(this.bg);

//因为上面的图片是截取的 所以右边可能没有图片了 这里补一个
this.rightBg = new laya.display.Sprite();
this.rightBg.graphics.drawTexture(laya.resource.Texture.createFromTexture(this.bgTexture,32*29,0,32,96), 0, 0, 32, 96);
this.rightBg.width = 32;
this.rightBg.y = -32;
this.addChild(this.rightBg);
}

//随机一个范围值
var _w = 32 * (3 + parseInt(19 * Math.random()));
this.bg.graphics.clear();
//这里用到了 laya.resource.Texture.createFromTexture 就是根据宽度和高度来截取一个图片并且返回一个Texture对象
this.bg.graphics.drawTexture(laya.resource.Texture.createFromTexture(this.bgTexture,0,0,_w,96), 0, 0, _w, 96);
this.rightBg.visible = true;
//这个是用来补上右边的图片 所以X轴坐标正好是bg的宽度
this.rightBg.x = _w;
// this.bg.graphics.drawTexture(this.bgTexture, 0, 0, 960, 96);

//计算一下右边还剩下多少 用来判断什么时候生成新的floor
//这里是通过游戏宽度 减去 固定 2个 32的宽度 再随机一个长度 这样 可以让地板时间点的出现 更加随机性
this.maxRight = 852 - 32 * 2 - 32 * parseInt(10 * Math.random());
//创建一个帧循环处理函数
Laya.timer.frameLoop(1, this, this.onLoop);
}
//在地板上面添加物品
_proto.addItem = function(){

}
//获取当前地板上面的所有物品
_proto.getItems = function(){
return ;
}

_proto.onLoop = function(){
//让地板的速度和移动比背景快一点
this.x -= 5 * 1.2;

//判断是否除了边界 如果出了 就通知生成新的floor 这里增加一个变量来判断当前是否已经通知外部了
//因为此处是一个循环的处理
if(!this.isOutComplete && (this.x + this.width) < this.maxRight){
this.isOutComplete = true;
this.event(Floor.OUT_COMPLETE, this);
}else if((this.x + this.width) < 0){
//判断整个floor是否不在屏幕里面了 如果不在了 移除当前floor
Laya.timer.clear(this, this.onLoop);
this.visible = false;
this.event(Floor.OUT_DIE, this);
}
}
})();

图片7.png

 
是不是很完美了
下节我们讲一下 玩家~~~ 谢谢~~~~ 
源码在附件里面~~~
已邀请:

Monica - 知识达人

赞同来自:

很细致的讲解,感谢分享~~O(∩_∩)O谢谢

cuixueying

赞同来自:

谢谢,非常棒!

curry1sgod

赞同来自:

非常nice

cinos

赞同来自:

非常详细,感谢楼主

thebackpacker

赞同来自:

有个问题没想明白,关于增加地板,产生条件是(this.x + this.width) < this.maxRight,this.x初始都是852,是正数,this.width是旧地板宽度也是正数,所以只有当前一个旧地板的this.x等于负值时等式才会成立,也就是前一个旧地板有部分图形超出左侧屏幕时,下一个地板才会从可视区域最右侧出来,但是实际运行,前一个旧地板还没超出左侧屏幕也就是还完整的显示在屏幕范围内,下一个地板已经产生了,有点没想明白,这个时候前一个旧地板的this.x还是正数啊,这个等式(this.x + this.width) < this.maxRight不成立,不会产生新地板,谢谢指导

xh4343

赞同来自:

QQ截图20180419171348.png
这个是派发事件么??父级类 用once监听   事件名还可以随便命名?

beyond5l

赞同来自:

谢谢,非常适合入门~

要回复问题请先

商务合作
商务合作