我的Ycc框架写了这么久,这回就用它来写个小游戏吧

看过我之前文章的朋友,会在代码中经常看见Ycc这个全局变量,这其实是我一直在写的一个框架。只是这个框架使用Ycc作为全局变量,类似于C++的命名空间,框架所有的类、属性和方法都在这个命名空间内。虽说之前也用过这个框架写一些示例,但示例终究是示例,一个示例只能展示框架中的某个功能点,并不能更好的展示框架全貌。想要更全面的展示框架的能力,这次索性就做个小游戏了。游戏名叫【别踩白块】。

开始

使用这个Ycc框架自然需要引入我们的Ycc源码了,如下:

<script src="../../build/ycc.js"></script>

当然我们还需要一个HTML文件来做显示,这里就粘贴个完整版的,如下:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>游戏-别踩白块</title>
   <link rel="stylesheet" href="../style.css">
   <style>
      body{
         font-size: 0;
     }
   </style>
</head>
<body>
    <-- 这里是舞台区 -->
</body>
</html>
<script src="../../build/ycc.js"></script>

<script>
    var ycc = new Ycc();
    // ... other code
</script>

可以看到,给body设置了font-size:0,这样可以去除canvas标签的一像素空隙。我们的代码都将写在最后一个script标签内。

初始化舞台

在开始之前,我们需要有一个舞台区域来显示我们的UI,我们使用如下代码来初始化。

if(!Ycc.utils.isMobile())
   alert("此示例在移动端查看效果更佳!");

var canvas = document.createElement('canvas');
canvas.width=document.documentElement.clientWidth;
canvas.height=document.documentElement.clientHeight;
document.body.appendChild(canvas);

在初始化之前我们借助Ycc的isMobile工具方法检测了移动端,并弹出了提示。

之后,使用js创建了canvas作为我们的舞台,并将其高宽设置为整个可视区的高宽。

这样设置之后,我们的舞台区就是用户的整个可视窗了。

设计场景

这个小游戏的思路很简单,场景相对也很少。

这里就设计了两个场景,一个是游戏的入口场景,就一个开始按钮,点击马上切换至游戏场景。

image.png

另一个是游戏场景

image.png

游戏进行场景的顶部显示分数和当前的速度。

如果游戏结束,那么会在游戏场景中显示一个弹出层,显示得分及重新开始按钮。

image.png

这就是整个场景的设计了,非常简单。

定义必要的全局变量

// ycc实例
var ycc = new Ycc().bindCanvas(canvas);
// 舞台的宽
var stageW = ycc.getStageWidth();
// 舞台的高
var stageH = ycc.getStageHeight();
// 所有的图片资源
var images = null;
// 所有音频资源
var audio = null;
// 当前场景
var currentScene = null;
// 绘制帧率
var frameRate = 60;

这里定义了很多必要的全局变量,这里全局变量的意思,即指两个场景都会用到的变量。

并且使用currentScene来保存当前游戏处于哪一个场景,初始没有场景,值为null。

除此之外,我们创建了ycc实例,并将其绑定至我们前面创建的舞台canvas上。

开启帧动画

// 开启心跳模块
ycc.ticker.start(frameRate);
// 监听每帧
ycc.ticker.addFrameListener(function () {
   currentScene && currentScene.update && currentScene.update();
   ycc.layerManager.reRenderAllLayerToStage();
});

只要创建了ycc实例,并绑定了舞台canvas,就可以调用Ycc的心跳模块(Ticker),用它来对界面进行绘制,绘制的频率根据帧率进行。

在每帧的回调方法中,我们直接调用ycc的图层管理器重新渲染所有图层。

只不过,这里的渲染没有任何内容,因为我们没有创建场景,也没有创建任何图层。

加载资源

由于游戏用到了图片和音乐,常规做法就是在项目初始化阶段将所有资源都预加载出来。这里的做法也是如此:

ycc.loader.loadResOneByOne([
   {name:"start",url:"./images/start.png"},
   {name:"restart",url:"./images/restart.png"},
],function (lise,imgs) {
   images = imgs;
   ycc.loader.loadResOneByOne([
      {name:'bg',url:"./audio/bg.mp3",type:"audio"},
      {name:'over',url:"./audio/over.wav",type:'audio'},
      {name:'tap',url:"./audio/tap.mp3",type:'audio'},
   ],function (audios) {
      audio=audios;
      // 新建场景,并马上重绘
      currentScene = new StartScene();
      ycc.layerManager.reRenderAllLayerToStage();
   });
});

借助于Ycc的加载器(Loader)分别加载了图片资源和音频资源。

这里调用了两次loadResOneByOne来分别处理图片和音频,当然也可以将其合并为一个,这里只是为了方便而已。

在两次加载都全部完成的回调中,我们新建了StartScene场景实例,赋值给currentScene,并马上重绘舞台。

至此,整个流程已经清晰了,接下来就是如何实现场景的问题了。

场景的实现

上面加载资源小节中,资源加载成功后,马上创建了StartScene场景,即游戏的开始界面。它的实现非常简单:

function StartScene(){
   var self = this;
    
   this.layer = ycc.layerManager.newLayer({enableEventManager:true});

   this.layer.addUI(new Ycc.UI.Image({
      // 计算UI的位置
      rect:new Ycc.Math.Rect(stageW/2-images.start.naturalWidth/2,stageH/2-images.start.naturalHeight/2,images.start.naturalWidth,images.start.naturalHeight),
      res:images.start,
      ontap:function (e) {
         self.delSelf();
         currentScene = new GameScene();
      }
   }));
}

/**
 * 删除自身
 */
StartScene.prototype.delSelf = function () {
   ycc.layerManager.deleteLayer(this.layer);
};

可以看到,这个场景仅仅只有一个layer属性和一个delSelf方法。

并且在图层中,只有一个UI元素,即Ycc.UI.Image,是开始游戏的按钮。这个UI元素绑定了一个tap事件,代表点击事件。

只要点击这个按钮,会马上删除当前场景,并新建新的场景GameScene赋值给currentScene。

这个GameScene就代表游戏场景,它的实现相对来说比较复杂,大致如下:

function GameScene(){

   // 游戏进行中的图层
   this.layer = ycc.layerManager.newLayer({enableEventManager:true});

   // 游戏结束后的弹出层
   this.overLayer = ycc.layerManager.newLayer({enableEventManager:true,show:false});

   // 列
   this.col = 4;
   // 行
   this.row = 3;

   // y轴总的偏移量
   this.offsetY = 0;

   // 每帧Y轴的步长
   this.spaceY = 20;

   // 最大速度
   this.maxSpaceY = 50;

   // 方框宽度
   this.rectW = parseInt(stageW/this.col);

   // 方框高度
   this.rectH = parseInt(stageH/this.row);

   // 初始时所有方块可能的y值
   this.rectYList = [];

   // 已偏移的方块个数
   this.offsetNumber=-1;
   // 列的白色线条UI
   this.colLines = [];
   // 行的白色线条UI
   this.rowLines = [];
   // 方块UI
   this.rects = [];

   // 当前得分
   this.score = 0;
   // 顶部得分和速度的文本UI
   this.scoreText=null;

   // 最终得分的文本UI
   this.finalScoreText=null;

   // 根据最终得分显示对应的提示文本
   this.finalTipText = null;

   // 游戏是否结束
   this.end = false;
   // 背景音乐
   this.bgm = ycc.loader.getResByName('bg',audio);
   // 游戏结束的音效
   this.gameOverBgm = ycc.loader.getResByName('over',audio);
   // 方块点中时的音效
   this.bgmTap = ycc.loader.getResByName('tap',audio);


   ///// 初始化
   this.createRect();
   this.createColLine();
   this.createRowLine();
   this.createTextScore();
   this.createGameOverLayer();

   if(this.bgm) {
      this.bgm.res.loop = true;
      this.bgm.res.play();
   }

}
// 添加竖线
GameScene.prototype.createColLine = function (){
    // ...
};
// 添加横线
GameScene.prototype.createRowLine= function (){
    // ...
};
// 创建方块
GameScene.prototype.createRect= function (){
    // ...
};
// 创建计分文本
GameScene.prototype.createTextScore= function (){
    // ...
};
// 创建游戏结束的弹层页
GameScene.prototype.createGameOverLayer= function (){
    // ...
};
// 每个方块的点击事件
GameScene.prototype.rectOnTap= function (){
    // ...
};
// 每帧的更新函数
GameScene.prototype.update= function (){
    // ...
};

鉴于篇幅原因,这里的代码就不展开讲解了,这里主要讲大致思路。

1、对于每一帧的更新函数update,我们会将横线和界面上的所有方块向下移动一定的像素值(spaceY),竖线不需要移动。

2、当最下方的方块超出舞台之后,我们将超出的那一行方块的Y轴坐标全部重新赋值,让它们位置回到顶部,这样就达到了重复利用舞台上的方块UI的目的。

注意,这里并不是超出后直接删除方块,并在顶部新增方块,原因在于浏览器的垃圾回收并不能由程序员手动触发,它是浏览器的默认行为。

我们代码删除方块往往就是赋值为null,但这样往往是删不干净的,对于这种删除和新增频率很高的程序,很容易造成内存泄露。

3、最下方的方块超出舞台时,需要判断是否游戏结束,若存在未点击的方块,那么游戏结束,直接修改游戏结束的弹层overLayer的属性show即可。

4、对于方块点击事件的回调函数rectOnTap,会在这里判断点击方块的颜色,并在指定颜色的方块上播放音效及修改得分。

对于以上代码的实现,感兴趣的朋友可以在文章末尾点击相应的链接进行查看。

写在最后

这个小游戏非常简单,用到的Ycc功能点包括:

1、资源加载器

2、图层系统

3、心跳系统

4、UI系统

5、事件系统

6、多媒体

基本上已经涵盖了目前框架的大部分功能,麻雀虽小,五脏俱全,也算一个相对综合的示例吧。

想玩小游戏的朋友请点击:http://www.lizhiqianduan.com/products/ycc/examples/game-do-not-touch-white-rect/

对于手机性能较差的朋友,这个链接可能会流畅一些:http://www.lizhiqianduan.com/products/ycc/examples/game-do-not-touch-white-rect/index2.html

查看小游戏源码的朋友请点击:https://github.com/lizhiqianduan/ycc/blob/develop/examples/game-do-not-touch-white-rect/index.html

想使用Ycc来写小游戏的朋友请点击:https://github.com/lizhiqianduan/ycc/tree/develop

有github账号的朋友,希望可以给我这个Ycc项目撒个花,一个人默默写了1年多,star数目还没破两位数,鬼知道我是怎么坚持下来的。万分感谢!

项目地址:https://github.com/lizhiqianduan/ycc



打赏作者

发表评论

电子邮件地址不会被公开。 必填项已用*标注