HTML5游戏_基于DOM平台跳跃小游戏开发
浮动的金币精灵
- 视频讲解
HTML5游戏
-
效果图
-
本章新知识
-
Math.PI 返回圆周率π.
-
Math.sin() 函数返回一个数值的正弦值
-
Math.random() 函数返回一个浮点数,伪随机数在范围从0到小于1,也就是说,从0(包括0)往上,但是不包括1(排除1)
-
矢量 在二维及以上维度既有大小又有方向的量为矢量.我们在工具函数.js里定义一个矢量函数用来设置游戏里所有精灵的矢量,如坐标,大小等
在 工具函数.js 中添加代码
/**
*@描述: 生成html标签 函数
*@传参: 名称(html标签类型) 类名称(html标签类名)
*@参数:
*@返回值: 标签(html标签)
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
function 生成html标签(名称, 类名称)
{
//定义变量 标签 保存创建的html标签
var 标签 = document.createElement(名称);
//类名称不为空
if (类名称)
{
//修改html标签的class属性
标签.className = 类名称;
}
//返回标签
return 标签;
}
/**
*@描述: 矢量 函数 在二维及以上维度既有大小又有方向的量为矢量
*@传参: x(x方向) y(y方向)
*@参数: x(x方向) y(y方向)
*@返回值: 无
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
function 矢量(x, y)
{
//定义属性 x方向
this.x = x;
//定义属性 y方向
this.y = y;
}
/**
*@描述: 矢量 添加 偏移 方法
*@传参: 偏移矢量(偏移矢量)
*@参数:
*@返回值: 位移后矢量
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
矢量.prototype.偏移 = function(偏移矢量)
{
//返回偏移后的矢量
return new 矢量(this.x + 偏移矢量.x, this.y + 偏移矢量.y);
};
- 做游戏的过程其实跟拍电影是一样的,电影里你要布置场景,游戏里你要制作地图场景,电影里你要有演员男女主角,游戏里你要有精灵玩家,电影要有演员反派,游戏里要有精灵敌人,电影要有道具宝箱,游戏里要有精灵宝箱
- 游戏图层 用来存放显示某一类精灵的区域,可以理解成ps的图层,所有精灵都放到图层里方便管理
- 精灵 简单地说从游戏制作角度,所有在游戏显示的元素,皆可称为精灵.比如玩家/敌人/子弹/宝箱/包括地图也是.同时每个精灵可以有自己的各种属性,如动作/动画/声音/事件等,所以精灵也就成了对象
- 本章内容:
- 提取金币精灵 思路:在之前的地图场景里我们绘制的金币只是地图场景,现在我们要把金币变成游戏里的单独精灵,可以漂浮在空中等,那首先我们要把金币从解析地图时单独提取出来并解析成一个金币精灵的对象,然后把金币精灵放到精灵层里
- 定义精灵集合对象,用来存放游戏里所有精灵,并在里面创建金币精灵
- 精灵行为 是一个精灵的属性,用来让精灵做出动画行为,比如Dome中金币的基础动画就是上下小范围浮动,看起来像漂浮的金币一样
精灵.js
//定义全局对象 精灵集合
var 精灵集合 =
{
"o": 金币精灵
};
/**
*@描述: 金币精灵 函数
*@传参: 坐标 (矢量)
*@参数: 初始坐标,缩放,晃动值,类型
*@返回值: 无
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
function 金币精灵(坐标)
{
//定义属性 初始坐标
this.初始坐标 = this.坐标 = 坐标;
//定义大小 因为游戏场景中每格大小为15*15,但金币的css中加了3px的边框所以为21*21,所以要缩放到15*15.15/21=0.7即缩放0.7倍
this.大小 = new 矢量(0.7, 0.7);
//定义属性 晃动值 初始的晃动系数
/*
PI是180°,PI*2=360° 随机数(0~1)*PI*2 就是在0°~360°中随机取一个角度
范围即0*3.1415*2 ~ 0.9999*3.1415*2
*/
this.晃动值 = Math.random() * Math.PI * 2;
//定义属性 类型
this.类型 = "金币";
}
/**
*@描述: 金币精灵 添加 行为 方法
*@传参: 每帧执行时间 (每帧执行时间)
*@参数: 晃动速度,晃动区域,晃动坐标y
*@返回值: 无
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
金币精灵.prototype.行为 = function(每帧执行时间)
{
//定义定量 晃动速度
const 晃动速度 = 8;
//定义定量 晃动区域
const 晃动区域 = 0.07;
//晃动值自加 每帧执行时间*晃动速度=每帧移动距离
this.晃动值 += 每帧执行时间 * 晃动速度;
//定义定量 晃动坐标y
const 晃动坐标y = Math.sin(this.晃动值) * 晃动区域;
//设置坐标偏移后的值
this.坐标 = this.初始坐标.偏移(new 矢量(0, 晃动坐标y));
};
修改 生成地图数据.js
在 生成地图数据 函数中添加 精灵数组 属性用来存放精灵
在 生成地图数据 函数中添加 动画 函数 来执行每帧精灵所执行的行为
/**
*@描述: 生成地图数据 函数
*@传参: 地图 (地图数组的一项,如 地图数组[0] )
*@参数: 宽,高,元素数组,精灵数组
*@返回值: 无
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
function 生成地图数据(地图)
{
//定义属性 宽 为地图[]数组的长度
this.宽 = 地图[0].length;
//定义属性 高 为地图数组的长度
this.高 = 地图.length;
//定义属性 元素数组 用来存放游戏里每个元素对应地图数据中的元素类型
this.元素数组 = [];
//定义 精灵数组 用来存放游戏里对应地图数据中的精灵
this.精灵数组 = [];
//遍历地图数组,识别地图元素
for (var y = 0; y < this.高; y++)
{
//定义变量 行 来存放地图里一行的数据
const 行 = 地图[y];
//定义数组 元素行 来存放解析地图一行数据后的元素类型
let 元素行 = [];
//遍历一行地图数据
for (var x = 0; x < this.宽; x++)
{
//定义变量 元素 来存 行[] 里的数据
const 元素 = 行[x];
//定义变量 元素类型 来存解析后的元素类型
let 元素类型 = null;
//定义变量 精灵内容 来保存在精灵集合对象中找出元素对应的属性
const 精灵内容 = 精灵集合[元素];
//判断元素
if (精灵内容)
{
//如果是精灵集合中元素,添加到精灵数组
this.精灵数组.push(new 精灵内容(new 矢量(x, y), 元素));
}
else if (元素 === "x")
{
元素类型 = "墙体";
}
else if (元素 === "@")
{
元素类型 = "玩家";
}
else if (元素 === "!")
{
元素类型 = "熔岩";
}
else if (元素 === "|")
{
元素类型 = "熔岩";
}
else if (元素 === "=")
{
元素类型 = "熔岩";
}
else if (元素 === "v")
{
元素类型 = "熔岩";
}
//将元素类型添加到元素行
元素行.push(元素类型)
}
//将一行数据添加到元素数组
this.元素数组.push(元素行);
}
//向控制台打印 元素数组 内容
console.log(this.元素数组);
this.关卡状态 = this.完成延时 = null;
}
/**
*@描述: 生成地图数据 添加 动画 方法
*@传参: 每帧执行时间(每帧执行时间)
*@参数:
*@返回值: 无
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
生成地图数据.prototype.动画 = function(每帧执行时间)
{
//定义变量 最大每帧执行时间=0.05
const 最大每帧执行时间 = 0.05;
//循环查看是否需要补帧动画
while (每帧执行时间 > 0)
{
//定义变量 当前帧执行时间 如果每帧执行时间大于最大每帧执行时间则取最大每帧执行时间0.05,即秒20帧
const 当前帧执行时间 = Math.min(每帧执行时间, 最大每帧执行时间);
//循环遍历 精灵数组
this.精灵数组.forEach(function(精灵)
{
//依次执行精灵.行为方法
精灵.行为(当前帧执行时间);
}, this);
//结合循环条件查看是否需要补帧动画
每帧执行时间 -= 当前帧执行时间;
}
};
修改 生成关卡.js 的 生成关卡 函数添加 精灵层属性
给 生成关卡 函数添加 画精灵层 方法 和 画精灵 方法 用来在每帧重绘精灵和精灵图层
/**
*@描述: 生成关卡 方法
*@传参: 父框架(生成关卡的所在标签) 新关卡地图(生成关卡地图 对象)
*@参数: 游戏场景,关卡地图数据,精灵层
*@返回值: 无
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
function 生成关卡(父框架, 新地图数据)
{
//添加游戏场景 div
this.游戏场景 = 父框架.appendChild(生成html标签("div", "游戏场景"));
//添加关卡地图数据=生成的新关卡地图数据
this.关卡地图数据 = 新地图数据;
//将关卡地图数据中的table地图画出并添加到游戏场景中
this.游戏场景.appendChild(this.画地图());
//添加精灵层
this.精灵层 = null;
//画精灵层
this.画精灵层();
}
/**
*@描述: 生成关卡 添加 画地图 方法
*@参数: 无
*@返回值: 地图表格(table标签)
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
生成关卡.prototype.画地图 = function()
{
//生成地图table
const 地图table = 生成html标签("table", "地图");
//设置地图table的css样式宽为 关卡地图数据.宽 * 尺寸
地图table.style.width = this.关卡地图数据.宽 * 尺寸 + "px";
//设置地图table的css样式高为 关卡地图数据.高 * 尺寸
地图table.style.height = this.关卡地图数据.高 * 尺寸 + "px";
//遍历关卡地图数据.方格数组来定义对应元素的表格
this.关卡地图数据.元素数组.forEach(function(行)
{
//生成地图table一行 行tr
const 行tr = 地图table.appendChild(生成html标签("tr"));
//设置行tr的css样式高为 尺寸
行tr.style.height = 尺寸 + "px";
//遍历行 根据元素类型标注到td标签的ClassName中,使得td标签显示出对应的CSS样式变成不同的方块
行.forEach(function(元素类型)
{
//生成每一个单元格 行tr并指定class=元素类型
行tr.appendChild(生成html标签("td", 元素类型));
});
});
//返回地图table
return 地图table;
};
/**
*@描述: 生成关卡 添加 画精灵层 方法
*@参数: 无
*@返回值: 无
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
生成关卡.prototype.画精灵层 = function()
{
//判断精灵层是否存在
if (this.精灵层)
{
//精灵层存在删除精灵层
this.游戏场景.removeChild(this.精灵层);
}
//为精灵层添加精灵
this.精灵层 = this.游戏场景.appendChild(this.画精灵());
};
/**
*@描述: 生成关卡 添加 画精灵 方法
*@参数: 无
*@返回值: 精灵内容(div标签)
*@创建人: 王天宇
*@创建时间:2021/02/24
*@修改人和其它信息:
**/
生成关卡.prototype.画精灵 = function()
{
//定义变量 精灵层内容
const 精灵层div = 生成html标签("div", "精灵层");
//遍历精灵数组
this.关卡地图数据.精灵数组.forEach(function(精灵)
{
//画出精灵div
const 精灵div = 精灵层div.appendChild(生成html标签("div", "精灵 " + 精灵.类型));
//定义精灵div的样式宽
精灵div.style.width = 精灵.大小.x * 尺寸 + "px";
精灵div.style.height = 精灵.大小.y * 尺寸 + "px";
精灵div.style.left = 精灵.坐标.x * 尺寸 + "px";
精灵div.style.top = 精灵.坐标.y * 尺寸 + "px";
});
//返回精灵层div
return 精灵层div;
}
然后再 css样式.css 里添加 精灵 的样式
/*背景颜色*/
body
{
background: #222;
}
/*游戏场景*/
.游戏场景
{
/*设置为相对定位元素*/
position: relative;
/*溢出的元素会被修建并且不可见*/
overflow: hidden;
}
/*地图*/
.地图
{
/*设置表格布局列宽由表格宽度和列宽设定*/
table-layout: fixed;
/*设置相邻单元格的边框间距为0*/
border-spacing: 0;
/*溢出的元素会被修建并且不可见*/
overflow: hidden;
}
.地图 td
{
/*4边padding为0*/
padding: 0;
}
/*墙体*/
.墙体
{
/*墙体背景色#444*/
background: #444;
/*墙体有3xp宽的线条边框,颜色是#333*/
border: solid 3px #333;
/*定义宽度高度分别应用到内容框*/
box-sizing: content-box;
}
/*熔岩*/
.熔岩
{
/*熔岩的颜色*/
background: #e55;
box-shadow: 0px 0px 15px #e55;
/*熔岩方块置顶 不被墙体遮挡*/
position: relative;
z-index: 100;
}
/*金币*/
.金币
{
/*金币的颜色*/
background: #e2e838;
/*定义边框圆角*/
border-radius: 50%;
/*金币有3xp宽的线条边框,颜色是#999900*/
border: solid 3px #999900;
box-shadow: 0px 0px 5px #ffff00;
}
/*玩家*/
.玩家
{
/*玩家的颜色*/
background: #335699;
/*定义玩家阴影*/
box-shadow: 0px 0px 5px #0099ff inset;
}
/*精灵*/
.精灵
{
/*position 属性规定元素的定位类型。
生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。
元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。*/
position: absolute;
}
最后在html代码中添加上一章的 运行帧动画,并在里面添加 新地图数据.动画 和 新关卡.画精灵层
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>html跳跃小游戏</title>
<!--CSS样式-->
<link rel="stylesheet" type="text/css" href="./css/css样式.css">
</head>
<body>
<h3 id="fps" style="color: #ffffff;">FPS=0</h3>
<!--JS脚本-->
<script type="text/javascript" src="./js/精灵.js"></script>
<script type="text/javascript" src="./js/地图数组.js"></script>
<script type="text/javascript" src="./js/工具函数.js"></script>
<script type="text/javascript" src="./js/生成地图数据.js"></script>
<script type="text/javascript" src="./js/生成关卡.js"></script>
<script type="text/javascript" src="./js/运行帧动画.js"></script>
<script>
//初始化程序
function 初始化()
{
//开始第一个关卡
开始关卡(0);
}
function 开始关卡(n)
{
//生成地图数据
var 新地图数据 = new 生成地图数据(地图数组[n]);
//生成关卡
var 新关卡 = new 生成关卡(document.body, 新地图数据);
//运行帧动画
运行帧动画(function(每帧执行时间)
{
/*这个方法是游戏里每帧运行的所有内容,包括动画/判断碰撞/按键监听等*/
//播放关卡地图里精灵一帧动画动作
新地图数据.动画(每帧执行时间);
//重新绘制一帧关卡
新关卡.画精灵层(每帧执行时间);
//计算每秒帧数并保留两位小数
document.getElementById("fps").innerHTML = "FPS=" + (1 / 每帧执行时间).toFixed(2);
//执行每帧动作方法 返回false时结束运行帧动画, 返回其他则继续执行动画
return true;
});
}
初始化();
</script>
</body>
</html>
运行html页面,就可以看到金币在上下浮动了
B站讲解视频:https://www.bilibili.com/video/BV1Kf4y147Xk/