js canvas游戏初级demo-躲避障碍物
在线演示地址 http://139.196.97.20:3100/html/game_demo.html
继上次js canvas游戏初级demo-上下左右移动(https://www.cnblogs.com/lzs-888/p/7427440.html),之后,对其新加了矩形下落和碰撞检测功能
1.头像移动
设置两个按键监听事件(keydown - 按下,keyup - 松开),每当按下时就将按下的键的state改为true,松开则相反,
然后,设置了一个定时器,每一定时间会读取这些按键state,根据state和速度进行头像位置坐标的调整,并且擦除头像,重新画上
2.矩形下落
与头像移动类似,只是擦除和绘制有多个(本代码中用设置多个定时器来维护这些矩形的绘制),并且横坐标和下落速度在一定范围内随机
3.碰撞检测
这个直接看代码,本代码写的有点啰嗦
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>按键盘的上下左右躲开这些黑色障碍物</title>
</head>
<style>
#canvas_dom{border: 1px solid #000;margin: 0 auto;display: block;background: #fff;}
.title{text-align: center;}
#game_time{text-align: center;}
</style>
<body>
<h1 class="title">按键盘的上下左右躲开这些黑色障碍物</h1>
<h2 id="game_time">你坚持的时间:<span>0</span>S</h2>
<canvas id="canvas_dom"></canvas>
<script>
/*
ddmm
2018/07/13 1.头像移动
设置两个按键监听事件(keydown - 按下,keyup - 松开),每当按下时就将按下的键的state改为true,松开则相反,
然后,设置了一个定时器,每一定时间会读取这些按键state,根据state和速度进行头像位置坐标的调整,并且擦除头像,重新画上 2.矩形下落
与头像移动类似,只是擦除和绘制有多个(本代码中用设置多个定时器来维护这些矩形的绘制),并且横坐标和下落速度在一定范围内随机 3.碰撞检测
这个直接看代码,本代码写的有点啰嗦
*/
//全局变量
var g = { ctx: null,//画布会话上下文
itv_ids: [],//用于记录定时器id,便于一起clear掉,减少内存消耗
game_over: false,//游戏是否结束
refreshNumForSec: 60,//每秒画面刷新次数
game_time: 0, //游戏坚持的时间
canvasWidth: 1000,//画布宽度
canvasHeight: 700,//画布高度
faceDataUrl: 'http://www.200ok.fun:3100/images/wudier.png',//头像地址
img: null,//头像图片引用
faceWidth: 80,
faceHeight: 82.5,
faceX: 0,//头像位置x坐标(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
faceY: 0,//头像位置y坐标(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
faceLastX: 0,//头像上一次位置x坐标,用于擦除(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
faceLastY: 0,//头像上一次位置y坐标,用于擦除(其初始值会在首次加载被initFacePos方法计算得出,故设置是无效的)
keyRight: false,//是否按了→
keyLeft: false,//是否按了←
keyUp: false,//是否按了↑
keyDown: false,//是否按了↓
faceSpeed: 15,//头像每次刷新画布移动的像素
rectW: 200,//矩形的宽度
rectH: 20,//矩形的高度
rectSpeedMin: 3,//方块速度最小值
rectSpeedMax: 15,//方块速度最大值
rectFlowNumForSec: 1,//每秒生成矩形个数
}; _main();//主函数 //获取画布上下文
function getCtx(){
if(!g.ctx){
//获取dom
var canvas = document.querySelector('#canvas_dom'); //设置宽高
canvas.width = g.canvasWidth;
canvas.height = g.canvasHeight; //获取会话上下文
g.ctx = canvas.getContext('2d');
} return g.ctx;
} function _main(){
getCtx();//获取画布上下文 //获取头像图片
g.img = new Image();
g.img.src = g.faceDataUrl;
g.img.width = g.faceWidth;
g.img.height = g.faceHeight;
g.img.onload = function(){//图片加载完成
g.img.style.border = "1px solid #000";
initFacePos();
} //监听keydown事件
window.addEventListener('keydown',function(e){
var k = e.key;
stateJudge(k,true);//修改按键state
}); //监听keyup事件
window.addEventListener('keyup',function(e){
var k = e.key;
stateJudge(k,false);//修改按键state
}); //设置定时器,1秒重绘 g.refreshNumForSec 次头像
g.itv_ids.push(
setInterval(function(){
if(!g.game_over){
moveJudge();
//擦除上一次画的
g.ctx.clearRect(g.faceLastX,g.faceLastY,g.img.width,g.img.height);
//绘制之前保存一下位置,用于下次擦除
g.faceLastX = g.faceX;
g.faceLastY = g.faceY;
//绘制头像
g.ctx.drawImage(g.img,g.faceX,g.faceY,g.img.width,g.img.height);
}
},1000/g.refreshNumForSec)
); //每1秒生成一个随机位置,随机速度的矩形往下掉
g.itv_ids.push(
setInterval(function(){
var max = g.canvasWidth - g.rectW;//画布宽度 - 矩形宽度
var x = get_random_num(0,max);//获取x坐标随机值
var speed = get_random_num(g.rectSpeedMin,g.rectSpeedMax);//获取速度随机值
drawTheRect(x,0,g.rectW,g.rectH,speed);
},1000/g.rectFlowNumForSec)
); //数秒,改dom
g.itv_ids.push(
setInterval(function(){
g.game_time++;
document.querySelector('#game_time span').innerText = g.game_time;
},1000)
);
} //根据画布和头像宽高,初始化头像位置,将其画于底部中间位置
function initFacePos(){
g.faceX = g.canvasWidth/2 - g.img.width/2;
g.faceY = g.canvasHeight - g.img.height;
g.faceLastX = g.faceX;
g.faceLastY = g.faceY;
g.ctx.drawImage(g.img,g.faceX,g.faceY,g.img.width,g.img.height);//绘制头像
} /**
* [checkRectImpact 碰撞检测,如果所提供的坐标点碰撞则返回true,否则返回false]
* - - 没看过别人怎么写的,写的有点啰嗦
* x1 x2 y1 y2 分别为第一个矩形(下称矩形A)的左边横坐标,右边横坐标,上边纵坐标,下边纵坐标
* a1 a2 b1 b2 分别为第二个矩形(下称矩形B)的左边横坐标,右边横坐标,上边纵坐标,下边纵坐标
* 坐标点如下图所示
x1 x2
———————— y1
| |
| |
| |
———————— y2 a1 a2
———————— b1
| |
| |
| |
———————— b2
*/
function checkRectImpact(x1,x2,y1,y2,a1,a2,b1,b2){
//分别为 左上角碰到 || 右上角碰到 || 右下角碰到 || 左下角碰到 || 矩形B大于矩形A且矩形B从下至上包含矩形A穿过 || 矩形B大于矩形A且矩形B从上至下包含矩形A穿过 || 矩形A大于矩形B且矩形A从下至上包含矩形B穿过 || 矩形A大于矩形B且矩形A从上至下包含矩形B穿过
return (x1 <= a1 && x2 >= a1 && y1 <= b1 && y2 >= b1) || (x1 <= a2 && x2 >= a2 && y1 <= b1 && y2 >= b1) || (x1 <= a2 && x2 >= a2 && y1 <= b2 && y2 >= b2) || (x1 <= a1 && x2 >= a1 && y1 <= b2 && y2 >= b2) || (x1 >= a1 && x2 <= a2 && y2 >= b1 && y1 <= b1) || (x1 >= a1 && x2 <= a2 && y2 >= b2 && y1 <= b2) || (a1 >= x1 && a2 <= x2 && b2 >= y1 && b1 <= y1) || (a1 >= x1 && a2 <= x2 && b2 >= y2 && b1 <= y2);
} //获取某个某个区间内的随机整数 ,获取到的值域为[min,max)
function get_random_num(min,max){
if(/^-?\d+$/.test(min) && /^-?\d+$/.test(max) && max>min){
return parseInt(Math.random()*(max - min) + min);
}else{
return false;
}
} /**
* [drawTheRect 画矩形]
* @param {[type]} x [矩形距离左边框的距离]
* @param {[type]} y [矩形距离上边框的距离]
* @param {[type]} w [矩形的宽度]
* @param {[type]} h [矩形的高度]
* @param {[type]} speed [每一次重绘画面,矩形下坠的距离,即下坠速度]
* @return {[type]} [description]
*/
function drawTheRect(x,y,w,h,speed){
var itv_id = setInterval(function(){
if(!g.game_over){
//绘制之前保存一下位置,用于下次擦除
var lastX = x;
var lastY = y;
// var speed = get_random_num(3,120); //经G民同学提示,可以考虑每次重绘方块时给其变速
y += speed;
g.ctx.clearRect(lastX,lastY,w,h); //擦除
g.ctx.fillRect(x,y,w,h); //画
if(checkRectImpact(g.faceX,g.faceX + g.img.width,g.faceY,g.faceY + g.img.height,x,x+w,y,y+h)){//碰撞检测
game_over();
return;
}
if(y > g.canvasHeight){//如果已经超过了画布的高度那么移除这个定时器,减少内存消耗
clearInterval(itv_id);
}
}
},1000/g.refreshNumForSec);
g.itv_ids.push(itv_id);//存到全局变量
} //游戏结束
function game_over(){
clearAllInterval();
g.game_over = true;
setTimeout(function(){//如果不延迟可能会导致上一次绘画未完成就被阻塞了
if(confirm("游戏结束,你坚持了" + g.game_time + "S" + ",继续努力!\n重新开始游戏请点'是'")){
window.location.reload();
}
},50);
} //clear所有定时器
function clearAllInterval(){
for(var i in g.itv_ids){
r = g.itv_ids[i];
clearInterval(r);
}
} //根据按键状态修改头像的坐标
function moveJudge(){
if(g.keyRight === true){
g.faceX += g.faceSpeed;
}else if(g.keyLeft === true){
g.faceX -= g.faceSpeed;
}else if(g.keyUp === true){
g.faceY -= g.faceSpeed;
}else if(g.keyDown === true){
g.faceY += g.faceSpeed;
} //边界情况处理,不要让头像超出边界
if(g.faceX > g.canvasWidth - g.img.width){
g.faceX = g.canvasWidth - g.img.width;
}else if(g.faceX < 0){
g.faceX = 0;
}
if(g.faceY > g.canvasHeight - g.img.height){
g.faceY = g.canvasHeight - g.img.height;
}else if(g.faceY < 0){
g.faceY = 0;
}
} //根据按键修改状态
function stateJudge(k,v){
if(k == 'ArrowRight'){
g.keyRight = v;
}else if(k == 'ArrowLeft'){
g.keyLeft = v;
}else if(k == "ArrowUp"){
g.keyUp = v;
}else if(k == "ArrowDown"){
g.keyDown = v;
}
}
</script>
</body>
</html>
js canvas游戏初级demo-躲避障碍物的更多相关文章
- js canvas游戏初级demo-上下左右移动
大概流程就是监听状态变化擦除画布重绘 由于js监听时间变化的函数addEventListener只能达到每秒触发20次左右,也就是每秒20帧,看起来有点卡卡的 所以用定时器搞到每秒30帧 按上下左右键 ...
- 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理
[微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...
- HTML5 五子棋 - JS/Canvas 游戏
背景介绍 因为之前用c#的winform中的gdi+,java图形包做过五子棋,所以做这个逻辑思路也就驾轻就熟,然而最近想温故html5的canvas绘图功能(公司一般不用这些),所以做了个五子棋,当 ...
- 用html5+js实现掌机游戏赛车demo
最近无聊,用html5+js做了一个以前玩过的掌机赛车游戏,顺便学习一下画布的api以及巩固一下js基础. 游戏界面,没做什么美化. 游戏规则:游戏界面分为三列,黑色方块随机落下,红色方块可以在三列自 ...
- canvas游戏小试:画一个按方向键移动的圆点
canvas游戏小试:画一个按方向键移动的圆点 自己对canvas,但又有一颗做游戏的心TT.然后记录一下对canvas的学习吧,用一个按方向键控制的小圆点来做练习.(编程时用了一些es6的语法) ...
- canvas 从初级到XX 1# 部分非基础原生API的使用 [初级向]
标题canvas 从初级到XX,XX是因为本文随机都可能会太监,并不会支持到入土.请慎重的往下看. 对于canvas的介绍,随处都可以找到,也就不啰嗦太多了.就直奔主题了. 先看一段代码,以及实现的效 ...
- 转 原生js canvas实现苹果电脑mac OS窗口最小化效果
http://www.17sucai.com/pins/demo-show?id=2459 http://www.17sucai.com/pins/demo-show?id=2458 很多资料 ,前 ...
- chart.js & canvas
chart.js & canvas https://www.chartjs.org/samples/latest/ https://www.chartjs.org/samples/latest ...
- [JS,Canvas]日历时钟
[JS,Canvas]日历时钟 Html: <!doctype html> <html> <head> <meta charset="UTF-8&q ...
随机推荐
- 让iframe自适应高度-真正解决
需求:实现 iframe 的自适应高度,能够随着页面的长度自动的适应以免除页面和 iframe 同时出现滚动条的现象. (需要只有iframe出现滚动条) 本人一开始这么写:会造成只有主页面加载是设定 ...
- 20175204 张湲祯 2018-2019-2《Java程序设计》第四周学习总结
20175204 张湲祯 2018-2019-2<Java程序设计>第四周学习总结 教材学习内容总结 -第五章子类与继承要点: -子类与父类: 1.通过使用关键字extends来定义一个类 ...
- OSI七层模型与TCP/IP五层模型
博主是搞是个FPGA的,一直没有真正的研究过以太网相关的技术,现在终于能静下心学习一下,希望自己能更深入的掌握这项最基本的通信接口技术.下面就开始搞了. 一.OSI参考模型 今天我们先 ...
- filebeat_config
Filebeat Prospector filebeat.prospectors: - input_type: log paths: - /var/log/apache/httpd-*.log doc ...
- [转]PuTTY字体颜色设置
转载于 https://blog.csdn.net/cyd_shuihan/article/details/77836290 用putty登录Linux,默认配色方案看不清,我们可以自己设置新的字体大 ...
- 使用Java API操作HDFS文件系统
使用Junit封装HFDS import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org ...
- 开发快捷键(Eclipse,STS)
Eclipse 常用快捷键 Eclipse的编辑功能非常强大,掌握了Eclipse快捷键功能,能够大大提高开发效率.Eclipse中有如下一些和编辑相关的快捷键. 1. [ALT+/] 此快捷键为 ...
- Java Callable使用
1. 创建线程的三种方式: 继承Thread,重写run方法 实现Runnable接口,重新run方法 实现Callable接口,重写call方法 2. Callable接口实际上是属于Executo ...
- Python实现简单的HttpServer
要写一个类似tomcat的简易服务器,首先需弄清楚这几点: 1. 客户端(Client)和服务端(Server)的角色及作用 角色A向角色B请求数据,这时可以把A视为客户端,B视为服务端.客户端的主要 ...
- Spring AOP概念理解
1.我所知道的aop 初看aop,上来就是一大堆术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下子让你不知所措,心想着:怪不得很多人都和我说aop多难多难.当我看进去以后 ...