这是一个系列课程,我们将会用不同的机器学习算法,让机器学会玩不同的游戏。
今天要让机器玩的游戏是:陨石坠落(点击试玩)。
课程中,我们将围绕陨石坠落点预测问题,建立机器学习模型。
我们将会用到线性回归、梯度下降、Z-Score数据标准化等机器学习算法。
课程中,我们将使用Javascript作为开发语言,游戏构建在我自己写的一个简单的H5游戏引擎上。
你可以直接登陆我的网站,进行开发,无需安装开发软件和配置开发环境。
打开地址 http://www.insideai.cn/lesson_game1.html 就可以开发了。
课程会分成两个部分:
1. 游戏制作 这部分你将用H5亲手制作一款简单的游戏
2.机器学习建模 这部分你将从零开始,构建一个机器学习模型,并动手实践出来
下面我们就先说下,如何用H5制作一款简单的游戏。
公元4096年,狮子座M1024星发生爆炸,大量陨石向地球飞来。如果陨石撞上地球,将会对地球造成毁灭性的冲击。所幸神盾局已经研制出能抵抗陨石冲击的神盾,但神盾没有办法自动锁定陨石。
因此神盾局找到了你,你是个计算机高手,能为神盾注入灵魂。可以让神盾具有预测能力。 为了配合你的工作,神盾局会实时检测陨石的位置,并将陨石数据告知给你。
你需要利用这些数据,给神盾建立机器学习模型,在陨石坠落到安全线之前,预测出陨石的坠落点,并移动至陨石的坠落。
注:陨石坠落的位置、速度和方向都是随机的,每一颗陨石都不一样。
游戏主要包含三个分镜头:
1. 游戏引导镜头 主要介绍故事背景和引导玩家开始游戏。
在制作镜头之前,我们需要先把游戏场景制作出来。游戏场景类似于游戏舞台,有了游戏场景后,游戏镜头才能播放出来。游戏场景的创建和初始化代码如下:
var scene = new DMScene("gameContainer");
scene.init();
注:上述代码是基于我写的H5游戏开发引擎编写的,因此需要登陆我的网站上进行开发,打开开发页面
下面我们就逐一把这些分镜头制作出来。
游戏引导镜头中,包含背景图片和引导文字,因此文字会开启逐字打印效果,因此我们还需要播放打字音效。
//DMImage为图片对象,参数分别是:游戏场景、图片地址、横坐标、纵坐标、图片宽度和图片高度
var guideImg = new DMImage(scene, "fall.jpg", 0, 0, scene.gwidth, scene.gheight);
var guideStr = "公元4096年,狮子座M1024星发生爆炸,大量陨石向地球飞来。如果陨石撞上地球,将会对地球造成毁灭性的冲击。" +
"所幸神盾局已经研制出能抵抗陨石冲击的神盾,但神盾没有办法自动锁定陨石。" +
"因此需要为神盾注入灵魂,让它自己锁定陨石,并移至降落点。"+
"经过千挑万选,最终发现只有你能胜任这个任务。" +
"点击任意区域,开始拯救地球吧!!!";
//DMTxt为文本对象,参数分别为:游戏场景、文本内容、横坐标、纵坐标、字体样式、
//字体颜色、行间距,是否开启逐字打印
var guideTxt = new DMTxt(scene, guideStr, 60, 80, "bold 36px '微软雅黑','宋体'"
,"white", 64, true);
//DMAudio为音频对象,参选分别为:音频地址,加载完成回调函数,是否循环播放
var writeMic = new DMAudio("write.mp3", null, "loop");
//游戏引导镜头
function guideGame(){
//将镜头中的元素添加到场景中
guideImg.addToScene();
guideTxt.addToScene();
//播放打字音效
writeMic.play();
//15秒后,暂停打字音效
setTimeout(function(){writeMic.pause();}, 15000);
}
游戏引导镜头制作完成后,调用函数guideGame,游戏场景中,就会播放引导镜头画面啦。
注:游戏中的图片和音频资源均来自于搜索引擎,如有侵权,请告知。游戏中,用到的图片和音频,我已放到我的网站上了。打开开发页面
游戏主体镜头中,包含陨石、神盾、得分文本和背景图片,此外我们还会循环播放游戏背景音乐。
var yunshi = new DMBox(scene,100,100, 10,10,"white");
//安全线位置,在屏幕的3/4处
var safeHeight = scene.gheight*0.75;
var shendun = new DMBox(scene, 0, safeHeight, 100, 30, "gray");
var earthImg = new DMImage(scene,"earth.jpg", 0, safeHeight-30, scene.gwidth,
scene.gwidth/4);
var score = 0;
var scoreTxt = new DMTxt(scene, "得分: " + score + " ", scene.gwidth - 120, 60,
"bold 24px '微软雅黑','宋体'", "red");
var bgMic = new DMAudio("bg.mp3", null, "loop");
function startGame(){
//清空游戏场景,清除上一个镜头中的元素
scene.clear();
//暂停上一个镜头中的音效
writeMic.pause();
earthImg.addToScene();
yunshi.addToScene();
shendun.addToScene();
scoreTxt.addToScene();
bgMic.play();
}
游戏主体镜头制作完成后,调用startGame就可以在游戏场景中,播放主体镜头了。
游戏结束镜头中,包括重玩引导文本和背景图片,其中重玩文本会开启逐字打印效果,因此需要播放打字音效。
var overImg = new DMImage(scene,"over.jpg",0,0,scene.gwidth,scene.gheight);
var overStr = "很遗憾,因为您的算法不当,陨石撞上了地球,地球发生了爆炸。不服!"
+ "点击任意区域,让时光倒退,重新开始!!!";
var overTxt = new DMTxt(scene, overStr, 60, 80, "bold 36px '微软雅黑','宋体'",
"white", 64, true);
function gameOver(){
scene.clear();
bgMic.pause();
overImg.addToScene();
//设置逐字打印的起始字符
overTxt.charAt = 0;
//设置逐字打印速度
overTxt.writeSpeed=10;
overTxt.addToScene();
writeMic.play();
setTimeout(function(){writeMic.pause();}, 10000);
}
gameOver();
好了,三个游戏镜头制作完成了,接下来,我们要已经玩家的交互行为,实现交互效果了。
目前为止,所有的元素都是静止的,下面我们就陨石动起来。这一节,我们实现陨石坠落特效。
陨石坠落的位置、速度和方向都是随机的,因此陨石的初始坐标和速度都要随机产生。
此外,为了提升游戏体验,陨石需要拖着尾巴,逐渐变亮,形成燃烧的效果,并播放坠落音效。
//使用线性过渡颜色,模拟陨石逐渐变亮
yunshi.useGrad("black","white",100, scene.gheight*0.6);
//陨石坠落音效
var fallMic = new DMAudio("fall.mp3");
function fall(){
//在游戏场景的中间80%区域随机产生横坐标
yunshi.x = Math.random()*scene.gwidth*0.8 + scene.gwidth*0.1;
yunshi.y = 0;
//水平方向的速度随机产生,可正可负,与场景宽度相关
yunshi.xSpeed = (Math.random() - 0.5)*scene.gwidth/80;
//竖直方向的速度随机产生,为正数,与场景高度相关
yunshi.ySpeed = Math.random()*scene.gheight/160 + scene.gheight/160;
//在陨石运动的反方向加上音频,模拟尾巴
yunshi.useShadow(20, "yellow", -1*yunshi.xSpeed, -1*yunshi.ySpeed);
fallMic.play();
}
这么复杂的效果,使用游戏引擎后,几句代码就完成了。打开开发页面
下面我们就把各个镜头串联起来,依据游戏逻辑,进行镜头切换。
在游戏引导镜头,用户点击时,进入游戏主体镜头,在游戏主体镜头,用户点击时,依据游戏状态,播放或暂停游戏。
在主体镜头中,如果陨石坠落到安全线以下,则认为游戏撞上了地球,游戏结束。
//定义游戏镜头索引,1引导镜头,2主体镜头,3结束镜头
var gameStatus = 1;
//添加鼠标点击监听事件
scene.setClickListener(function(){
if(gameStatus == 1){
startGame();
fall();
gameStatus = 2;
}else if(gameStatus == 2){
//如果有些正在进行,则暂停有些,否则播放游戏
if(scene.isPlay){
scene.pause();
}else{
scene.play();
}
}else if(gameStatus == 3){
scene.clear();
startGame();
fall();
gameStatus = 2;
}
});
//游戏场景回调函数,绘制每一帧之前都会调用该函数
function onDraw(){
if(gameStatus != 2){
return;
}
//判断陨石的纵坐标是否在安全线以下
if(yunshi.y > safeHeight + shendun.height){
gameOver();
gameStatus = 3;
}
}
//设置游戏场景的回调函数
scene.onDraw = onDraw;
好了,现在游戏可以在各个镜头之间切换了,但是神盾还没有办法移动,因此陨石总是撞上地球。
我们先用键盘控制神盾移动,后面我们再说如何让机器预测陨石坠落点,让神盾自主移动。
可以移动神盾后,我们就需要检测神盾与陨石是否发生了砰撞,如果发生了砰撞,我们需要播放陨石爆炸特效,并让神盾发出保护光环,播放撞击音效,此外我们还需对玩家进行计分。
//定义神盾移动速度
var shendunSpeed = scene.gwidth/100;
function goLeft(){
shendun.moveTo(shendun.x - shendunSpeed, shendun.y);
}
function goRight(){
shendun.moveTo(shendun.x + shendunSpeed, shendun.y);
}
//监听用户按键事件,如果是左箭头,则向左移动,如果是右箭头,则向右移动
window.addEventListener("keydown",function(e){
var keycode = scene.getKeyCode(e);
if(keycode == 37){
goLeft();
}else if(keycode == 39){
goRight();
}
}, false);
//重写场景回调函数,增加砰撞检测和陨石爆炸特效
function onDraw(){
if(gameStatus != 2){
return;
}
if(explordeTime > 0){
explode();
}else if(!yunshi.isInScene()){
fall();
}else if(yunshi.hitOnSprite(shendun)){
hitOn();
}else if(yunshi.y > safeHeight + shendun.height){
gameOver();
gameStatus = 3;
}
}
//陨石爆炸持续时长
var explordeTime = 0;
//陨石爆炸函数
function explode(){
explordeTime--;
yunshi.width = explordeTime;
yunshi.height = explordeTime;
if(explordeTime == 0){
yunshi.width = 10;
yunshi.height = 10;
shendun.useShadow(0,"blue",0,0);
fall();
}
}
//陨石击中神盾音效
var hitMic = new DMAudio("hit.mp3");
//陨石击中神盾处理函数
function hitOn(){
score++;
scoreTxt.txt = "得分: " + score;
shendun.useShadow(20, "blue", 0, -5);
hitMic.play();
explordeTime = 20;
yunshi.xSpeed = 0;
yunshi.ySpeed = 0;
}
//更新场景回调函数
scene.onDraw = onDraw;
好了,现在有些已经制作完成了,写了这么长事件代码,也有点小累了,玩会游戏吧。
下篇文章,我们将介绍如何针对游戏,进行机器学习建模,并利用机器学习模型,让神盾具有预测能力,自主移动到坠落点。