JS写小游戏(一):游戏框架
前言
前一阵发现一个不错的网站,都是一些用html5+css+js写的小游戏,于是打算学习一番,写下这个系列博客主要是为了加深理解,当然也有一些个人感悟,如果英文好可以直接Click Here.
概述
一般,小游戏都要关注两个问题:刷新和交互。因为游戏一般是动态的,所以需要不断刷新。JavaScript是单线程,如果用C语言写过贪吃蛇之类的小游戏,应该知道,单线程一般是挂起一个时间来达到动态效果。比如C语言的Sleep(),JS的setInterval()等。但是js还有一种更高性能的方法requestAnimationFrame。可以在网上找些资料进行学习,在此不做赘述。另一个就是交互,即用户需要通过鼠标、键盘控制游戏,从编程角度来书就是要添加对应事件的监听器。
以下,正式开始。
HTML5
先创建一个canvas画布:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Rembound.com Example</title>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<canvas id="viewport" width="640" height="480"></canvas>
</body>
</html>
JS
添加以下基本代码,代码详情可以看注释,最好单步调试加深理解:
//窗口加载完成后调用
window.onload = function() {
// 获取画布及context(上下文)
var canvas = document.getElementById("viewport");
var context = canvas.getContext("2d"); // 记录时间帧,这个最好通过单步调试来理解
var lastframe = 0;
var fpstime = 0;
var framecount = 0;
var fps = 0; // 初始化游戏,添加鼠标的监听事件
function init() {
canvas.addEventListener("mousemove", onMouseMove);
canvas.addEventListener("mousedown", onMouseDown);
canvas.addEventListener("mouseup", onMouseUp);
canvas.addEventListener("mouseout", onMouseOut); // 进入游戏主循环
main(0);
} // 主循环
function main(tframe) {
// 结束时继续调用main函数
window.requestAnimationFrame(main); // 更新游戏
update(tframe);
render();
} // 更新游戏状态,计算已经过去了的时间
function update(tframe) {
var dt = (tframe - lastframe) / 1000;
lastframe = tframe; //更新帧数计数器
updateFps(dt);
} function updateFps(dt) {
if (fpstime > 0.25) {
//计算帧数
fps = Math.round(framecount / fpstime); //重置时间
fpstime = 0;
framecount = 0;
} //增加帧时间、帧数
fpstime += dt;
framecount++;
} // 渲染(更新画布)
function render() {
drawFrame();
} //
function drawFrame() {
// 背景、边界
context.fillStyle = "#d0d0d0";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#e8eaec";
context.fillRect(1, 1, canvas.width-2, canvas.height-2); // 标题头
context.fillStyle = "#303030";
context.fillRect(0, 0, canvas.width, 65); // 标题
context.fillStyle = "#ffffff";
context.font = "24px Verdana";
context.fillText("HTML5 Canvas Basic Framework - Rembound.com", 10, 30); // 显示帧数
context.fillStyle = "#ffffff";
context.font = "12px Verdana";
context.fillText("Fps: " + fps, 13, 50);
} //鼠标监听
function onMouseMove(e) {}
function onMouseDown(e) {}
function onMouseUp(e) {}
function onMouseOut(e) {} // 获取鼠标位置
function getMousePos(canvas, e) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.round((e.clientX - rect.left)/(rect.right - rect.left)*canvas.width),
y: Math.round((e.clientY - rect.top)/(rect.bottom - rect.top)*canvas.height)
};
} // 游戏入口
init();
};
效果:

添加游戏元素
以上就是一个通用的游戏框架了,虽然它在不断刷新,但是没什么直观感受,以下建立一个简单游戏来感受一下:
......
var framecount = 0;
var fps = 0; // 游戏平面
var level = {
x: 1,
y: 65,
width: canvas.width - 2,
height: canvas.height - 66
}; // 小方块
var square = {
x: 0,
y: 0,
width: 0,
height: 0,
xdir: 0,
ydir: 0,
speed: 0
} // 分数
var score = 0; // 初始化游戏,添加鼠标的监听事件
function init() {
....
在init()函数中添加:
....
canvas.addEventListener("mouseout", onMouseOut);
// 初始化方块
square.width = 100;
square.height = 100;
square.x = level.x + (level.width - square.width) / 2;
square.y = level.y + (level.height - square.height) / 2;
square.xdir = 1;
square.ydir = 1;
square.speed = 200; // 初始化分数
score = 0; // 进入游戏主循环
main(0);
....
在update()函数中更新方块
....
//更新帧数计数器
updateFps(dt); // 基于时间移动方块
square.x += dt * square.speed * square.xdir;
square.y += dt * square.speed * square.ydir; // 处理碰撞
if (square.x <= level.x) {
// Left edge
square.xdir = 1;
square.x = level.x;
} else if (square.x + square.width >= level.x + level.width) {
// Right edge
square.xdir = -1;
square.x = level.x + level.width - square.width;
} if (square.y <= level.y) {
// Top edge
square.ydir = 1;
square.y = level.y;
} else if (square.y + square.height >= level.y + level.height) {
// Bottom edge
square.ydir = -1;
square.y = level.y + level.height - square.height;
}
...
render()函数中还要渲染方块
....
// 绘制方块
context.fillStyle = "#ff8080";
context.fillRect(square.x, square.y, square.width, square.height); // 绘制内部
context.fillStyle = "#ffffff";
context.font = "38px Verdana";
var textdim = context.measureText(score);
context.fillText(score, square.x+(square.width-textdim.width)/2, square.y+65);
...
添加鼠标事件
function onMouseDown(e) {
// 获取鼠标位置
var pos = getMousePos(canvas, e);
// 检查是否碰到了方块
if (pos.x >= square.x && pos.x < square.x + square.width &&
pos.y >= square.y && pos.y < square.y + square.height) {
// 增加分数
score += 1;
// 增加速度
square.speed *= 1.1;
// 随机给一个新的位置
square.x = Math.floor(Math.random()*(level.x+level.width-square.width));
square.y = Math.floor(Math.random()*(level.y+level.height-square.height));
// 随机方向
square.xdir = Math.floor(Math.random() * 2) * 2 - 1;
square.ydir = Math.floor(Math.random() * 2) * 2 - 1;
}
}
效果

完整源代码:Click Here
注:这只是众多小游戏合集中的一个,今后会继续添加。
JS写小游戏(一):游戏框架的更多相关文章
- 用JS写了一个打字游戏,反正我是通不了关
今天想写个简单的游戏, 打字游戏好像都没写过, 那么就写打字游戏吧, gamePad包含了关卡的信息, 可以用来调整给个关卡字符下落的速度: getRandom函数会返回一个字符对象, 这个对象包含了 ...
- 用原生js写小游戏--Flappy Bird
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 这个程序员有点牛,现场直接用JS写了个飞机游戏,半小时吸粉三千
程序员昨晚在b站直播的时用JavaScript代码写了一个飞机大战游戏,半小时不到粉丝关注就上千了. 今日就拿出来跟大家分享一下,对许多大佬来说做这个特效也不是很难,但是对于刚开始学习前端这方面还是有 ...
- 快速上手小程序的mpvue框架
一.什么是mpvue框架? mpvue 是一个使用 Vue.js 开发小程序的前端框架.框架基于 Vue.js 核心(所以建议熟练掌握vue再使用mpvue框架,否则还是建议去使用原生框架去写小程序) ...
- 分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”
这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业 ...
- pixi.js 微信小游戏 入手
pixi是什么?一款h5游戏引擎 优点:简单简洁性能第一 缺点:大多数用的国产三大引擎,pixi资料少,工具少, 为什么学,装逼 用pixi开发小游戏行吗? 行.但要简单处理下 下载官网上的 weap ...
- 原生JS实战:写了个斗牛游戏,分享给大家一起玩!
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5869953.html 该程序是本人的个人作品,写的不好,未经本人允许,请 ...
- 排名前10的H5、Js 3D游戏引擎和框架
由于很多人都在用JavaScript.HTML5和WebGL技术创建基于浏览器的3D游戏,所有JavaScript 3D游戏引擎是一个人们主题.基于浏览器的游戏最棒的地方是平台独立,它们能在iOS.A ...
- 原生js写的贪吃蛇网页版游戏特效
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <bo ...
随机推荐
- php设计模式 原型模式
原型模式与工程模式作用类似,都是用来创建对象. 与工程模式的实现不同,原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象.这样就免去了类创建时重复的初始化操作. 原型模式适用于大 ...
- yii2 实战教程之如何安装
作者:白狼 出处:http://www.manks.top/document/install.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则 ...
- iPhone被盗后续更新二:被换机!已取机!没扣住新机!怎么找新机呢?事发半年后跟进...
先说下情况 MEID/IMEI:3544 2706 9380 456 我的序列号:F17NL088G5MY 新的IMEI:3569 7606 5956 097 新的序列号:DNPNV69ZG5MY 我 ...
- leveldb源码分析--SSTable之Compaction
对于compaction是leveldb中体量最大的一部分,也应该是最为复杂的部分,为了便于理解我们首先从一些基本的概念开始.下面是一些从doc/impl.html中翻译和整理的内容: Level 0 ...
- android ListView 属性
android:divider="#fffff" 分割线颜色 android:dividerHeight="1px" 分割线高度 divider 分割线-去掉分 ...
- wubi安装ubuntukylin-14.04
自ubuntukylin-14.04发布以来一直想体验一下,正好五一休假有了时间,在经历了一番坎坷后终于安装成功.安装环境为Win7家庭高级版,至于采用wubi方式安装,只因Linux水平差和图省事, ...
- ::before和::after伪元素的用法
一.介绍 css3为了区分伪类和伪元素,伪元素采用双冒号写法. 常见伪类——:hover,:link,:active,:target,:not(),:focus. 常见伪元素——::first-let ...
- runv nslistener源码分析
nslistener的作用实质上就是将新的namespace里的veth网卡的配置信息通过proxy进程传输出来,并且利用该信息对tap进行相同的配置,最终用tap模拟新的namespace里的vet ...
- Android(Linux)控制GPIO方法二
前文<Android(Linux)控制GPIO的方法及实时性分析>主要使用Linux shell命令控制GPIO,该方法可在调试过程中快速确定GPIO硬件是否有问题,即对应的GPIO是否受 ...
- UVALive 4428 Solar Eclipse --计算几何,圆相交
题意:平面上有一些半径为R的圆,现在要在满足不与现有圆相交的条件下放入一个圆,求这个圆能放的位置的圆心到原点的最短距离. 解法:我们将半径扩大一倍,R = 2*R,那么在每个圆上或圆外的位置都可以放圆 ...