JavaScript实现2048小游戏

作者简介

作者名:
简介:CSDN博客专家,从事软件开发多年,精通Java、JavaScript,博主也是从零开始一步步把学习成长、深知学习和积累的重要性,喜欢跟广大ADC一起打野升级,欢迎您关注,期待与您一起学习、成长、起飞!

系列目录

1. JavaScript 贪吃蛇游戏
2. JavaScript 俄罗斯方块
3. JavaScript 扫雷小游戏
4. JavaScript 网红太空人表盘

效果图

实现思路

  1. 编写页面和画布代码。
  2. 绘制背景。
  3. 绘制好全部卡片。
  4. 随机生成一个卡片(2或者4)。
  5. 键盘事件监听(上、下、左、右键监听)。
  6. 根据键盘的方向,处理数字的移动合并。
  7. 加入成功、失败判定。
  8. 处理其他收尾工作。

代码实现

编写页面代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>2048</title><style>#box{width:370px;height:370px;position:absolute;margin:0 auto;left:0;right:0;top:1px;bottom:0;}.rebutton{position: absolute;top:370px;left:38%;}</style>
</head>
<body><div id='box'></div><button onclick="restart()" class='rebutton'>重开</button>
</body>
<script src="js/util.js"></script>
<script src="js/2048.js"></script>
<script type="text/javascript"></script>
</html>

添加画布

在2048.js编写代码

  1. 创建函数

    function G2048(){
    this.renderArr=[];//渲染数组
    this.cards=initCardArray();
    //游戏标记
    this.flag=‘start’;
    }
    //初始化数组
    function initCardArray(){
    var cards = new Array();
    for (var i = 0; i < 4; i++) {
    cards[i] = new Array();
    for (var j = 0; j < 4; j++) {
    //cards[i][j]=null;
    }
    }
    return cards;
    }

  2. 初始化和绘制背景代码(在2048.js中编写)

    //初始化
    G2048.prototype.init=function(el,musicObj){
    if(!el) return ;
    this.el=el;
    var canvas = document.createElement(‘canvas’);//创建画布
    canvas.style.cssText=“background:white;”;
    var W = canvas.width = 370; //设置宽度
    var H = canvas.height = 370;//设置高度

     el.appendChild(canvas);//添加到指定的dom对象中this.ctx = canvas.getContext('2d');this.draw();
    }
    //绘制入口
    G2048.prototype.draw=function(){//创建背景this.drawGB();//渲染到页面上this.render();}//创建背景
    G2048.prototype.drawGB=function(){var bg = new _.Rect({x:0,y:0,width:364,height:364,fill:true,fillStyle:'#428853'});this.renderArr.push(bg);
    }//渲染图形
    G2048.prototype.render=function(){var context=this.ctx;this.clearCanvas();    _.each(this.renderArr,function(item){item && item.render(context);});
    }
    //清洗画布
    G2048.prototype.clearCanvas=function() {this.ctx.clearRect(0,0,parseInt(this.w),parseInt(this.h));
    }
    
  3. 在页面代码中加入以下 js 代码

    var box = document.getElementById(‘box’);
    g2048.init(box);


运行效果:

绘制好全部卡片

  1. 创建Card

    //定义Card
    function Card(i,j){
    this.i=i;//下标i
    this.j=j;//下标j
    this.x=0;// x坐标
    this.y=0;// y坐标
    this.h=80;//高
    this.w=80;//宽
    this.start=10;//偏移量(固定值)
    this.num=0;//显示数字
    this.merge=false;//当前是否被合并过,如果合并了,则不能继续合并,针对当前轮
    //初始化创建
    this.obj = this.init();
    //创建显示数字对象
    this.numText = this.initNumText();
    }
    //初始创建
    Card.prototype.init=function(){
    return new _.Rect({x:this.x,y:this.y,width:this.w,height:this.h,fill:true});
    }
    //根据i j计算x y坐标
    Card.prototype.cal=function(){
    this.x = this.start + this.j*this.w + (this.j+1)5;
    this.y = this.start + this.i
    this.h + (this.i+1)*5;
    //更新给obj
    this.obj.x=this.x;
    this.obj.y=this.y;
    //设置填充颜色
    this.obj.fillStyle=this.getColor();

    //更新文字的位置
    this.numText.x = this.x+40;
    this.numText.y = this.y+55;
    this.numText.text=this.num;
    

    }
    //初始化显示数字对象
    Card.prototype.initNumText=function(){
    var font = “34px 思源宋体”;
    var fillStyle = “#7D4E33”;
    return new _.Text({x:this.x,y:this.y+50,text:this.num,fill:true,textAlign:‘center’,font:font,fillStyle:fillStyle});
    }
    //获取color
    Card.prototype.getColor=function(){
    var color;
    //根据num设定颜色
    switch (this.num) {
    case 2:
    color = “#EEF4EA”;
    break;
    case 4:
    color = “#DEECC8”;
    break;
    case 8:
    color = “#AED582”;
    break;
    case 16:
    color = “#8EC94B”;
    break;
    case 32:
    color = “#6F9430”;
    break;
    case 64:
    color = “#4CAE7C”;
    break;
    case 128:
    color = “#3CB490”;
    break;
    case 256:
    color = “#2D8278”;
    break;
    case 512:
    color = “#09611A”;
    break;
    case 1024:
    color = “#F2B179”;
    break;
    case 2048:
    color = “#DFB900”;
    break;

    default://默认颜色color = "#5C9775";break;
    }return color;
    

    }

    Card.prototype.render=function(context){
    //计算坐标等
    this.cal();
    //执行绘制
    this.obj.render(context);
    //是否绘制文字的处理
    if(this.num!=0){
    this.numText.render(context);
    }
    }

    }

  2. 创建卡片

    //创建卡片
    G2048.prototype.drawCard=function(){var that=this;var card;for (var i = 0; i < 4; i++) {for (var j = 0; j < 4; j++) {card = new Card(i,j);that.cards[i][j]=card;that.renderArr.push(card);}}
    }
    
  3. 调用绘制代码

    运行效果:

  4. 修改一下卡片的默认数字

随机生成一个卡片,2或者4

  1. 先把Card中 num 默认改成0
  2. 因为2跟4出现的比例是1:4,所以采用随机出1-5的数字,当是1的时候就表示,当得到2、3、4、5的时候就表示要出现数字2.
  3. 随机获取i,j 就可以得到卡片的位置,割接i,j取到card实例,如果卡片没有数字,就表示可以,否则就递归继续取,取到为止。
  4. 把刚才取到的数字,设置到card实例对象中就好了。

代码如下:

//随机创建一个卡片G2048.prototype.createRandomNumber=function(){var num = 0;var index = _.getRandom(1,6);//这样取出来的就是1-5 之间的随机数//因为2和4出现的概率是1比4,所以如果index是1,则创建数字4,否则创建数字2(1被随机出来的概率就是1/5,而其他就是4/5 就是1:4的关系)console.log('index==='+index)if(index==1){num = 4;}else {num = 2;}//判断如果格子已经满了,则不再获取,退出if(this.cardFull()){return ;}//获取随机卡片,不为空的var card = this.getRandomCard();//给card对象设置数字if(card!=null){card.num=num;}}//获取随机卡片,不为空的G2048.prototype.getRandomCard=function(){var i = _.getRandom(0,4);var j = _.getRandom(0,4);var card = this.cards[i][j];if(card.num==0){//如果是空白的卡片,则找到了,直接返回return card;}//没找到空白的,就递归,继续寻找return this.getRandomCard();}//判断格子满了G2048.prototype.cardFull=function() {var card;for (var i = 0; i < 4; i++) {for (var j = 0; j < 4; j++) {card = this.cards[i][j];if(card.num==0){//有一个为空,则没满return false;}}}     return true;}

draw方法中调用,表示打开游戏默认一个数字

运行效果:

加入键盘事件

同样要在draw方法中调用哦

 //按键的控制G2048.prototype.control=function(){var that=this;global.addEventListener('keydown',function(e){console.log(that.flag)if(that.flag!='start') return ;var dir;switch (e.keyCode){case 87://wcase 38://上dir=1;//上移动break;case 68://dcase 39://右dir=2;//右移动break;case 83://scase 40://下dir=3;//下移动break;case 65://acase 37://左dir=4;//左移动break;}//卡片移动的方法that.moveCard(dir);});}
  1. 加入移动逻辑处理代码

    //卡片移动的方法
    G2048.prototype.moveCard=function(dir) {
    //将卡片清理一遍,因为每轮移动会设定合并标记,需重置
    this.clearCard();

     if(dir==1){//向上移动this.moveCardTop(true);}else if(dir==2){//向右移动this.moveCardRight(true);}else if(dir==3){//向下移动this.moveCardBottom(true);}else if(dir==4){//向左移动this.moveCardLeft(true);}//移动后要创建新的卡片this.createRandomNumber();//重绘this.render();//判断游戏是否结束this.gameOverOrNot();
    }//将卡片清理一遍,因为每轮移动会设定合并标记,需重置
    G2048.prototype.clearCard=function() {var card;for (var i = 0; i < 4; i++) {//i从1开始,因为i=0不需要移动for (var j = 0; j < 4; j++) {card = this.cards[i][j];card.merge=false;}}
    }
    
  2. 加入上下左右处理逻辑

    //向上移动
    G2048.prototype.moveCardTop=function(bool) {
    var res = false;
    var card;
    for (var i = 1; i < 4; i++) {//i从1开始,因为i=0不需要移动
    for (var j = 0; j < 4; j++) {
    card = this.cards[i][j];
    if(card.num!=0){//只要卡片不为空,要移动
    if(card.moveTop(this.cards,bool)){//向上移动
    res = true;//有一个为移动或者合并了,则res为true
    }
    }
    }
    }
    return res;
    }
    //向右移动
    G2048.prototype.moveCardRight=function(bool) {
    var res = false;
    var card;
    for (var i = 0; i < 4; i++) {
    for (var j = 3; j >=0 ; j–) {//j从COLS-1开始,从最右边开始移动递减
    card = this.cards[i][j];
    if(card.num!=0){//只要卡片不为空,要移动
    if(card.moveRight(this.cards,bool)){//向右移动
    res = true;//有一个为移动或者合并了,则res为true
    }
    }
    }
    }
    return res;
    }

    //向下移动
    G2048.prototype.moveCardBottom=function(bool) {var res = false;var card;for (var i = 3; i >=0; i--) {//i从ROWS-1开始,往下递减移动for (var j = 0; j < 4; j++) {card = this.cards[i][j];if(card.num!=0){//只要卡片不为空,要移动if(card.moveBottom(this.cards,bool)){//下移动res = true;//有一个为移动或者合并了,则res为true}}}}return res;
    }//向左移动
    G2048.prototype.moveCardLeft=function(bool) {var res = false;var card;for (var i = 0; i < 4; i++) {for (var j = 1; j < 4 ; j++) {//j从1开始,从最左边开始移动card = this.cards[i][j];if(card.num!=0){//只要卡片不为空,要移动if(card.moveLeft(this.cards,bool)){//向左移动res = true;//有一个为移动或者合并了,则res为true}}}}return res;
    }
    
  3. 在Card中加入向上移动的处理逻辑

  1. 从第2行开始移动,因为第一行不需要移动。
  2. 只要卡片的数字不是0,就表示要移动。
  3. 根据 i-1 可以获取到上一个卡片,如果上一个卡片是空,则把当前卡片交换上去,并且递归,因为可能要继续往上移动。
  4. 如果当前卡片与上一个卡片是相同数字的,则要合并。
  5. 以上两种都不是,则不做操作。
//卡片向上移动Card.prototype.moveTop=function(cards,bool) {var i=this.i;var j=this.j;//设定退出条件if(i==0){//已经是最上面了return false;}//上面一个卡片var prev = cards[i-1][j];if(prev.num==0){//上一个卡片是空//移动,本质就是设置数字if(bool){//bool为true才执行,因为flase只是用来判断能否移动prev.num=this.num;this.num=0;//递归操作(注意这里是要 prev 来 move了)prev.moveTop(cards,bool);}return true;}else if(prev.num==this.num && !prev.merge){//合并操作(如果已经合并了,则不运行再次合并,针对当然轮)if(bool){bool为true才执行prev.merge=true;prev.num=this.num*2;this.num=0;}return true;}else {//上一个的num与当前num不同,无法移动,并退出return false;}}

  1. 在Card中加入其他3个方向的代码

    //向下移动
    Card.prototype.moveBottom=function(cards,bool) {
    var i=this.i;
    var j=this.j;
    //设定退出条件
    if(i3){//已经是最下面了
    return false;
    }
    //上面一个卡片
    var prev = cards[i+1][j];
    if(prev.num0){//上一个卡片是空
    //移动,本质就是设置数字
    if(bool){//bool为true才执行,因为flase只是用来判断能否移动
    prev.num=this.num;
    this.num=0;
    //递归操作(注意这里是要 prev 来 move了)
    prev.moveBottom(cards,bool);
    }
    return true;
    }else if(prev.num==this.num && !prev.merge){//合并操作(如果已经合并了,则不运行再次合并,针对当然轮)
    if(bool){bool为true才执行
    prev.merge=true;
    prev.num=this.num*2;
    this.num=0;
    }
    return true;
    }else {//上一个的num与当前num不同,无法移动,并退出
    return false;
    }

    }
    //向右移动
    Card.prototype.moveRight=function(cards,bool) {var i=this.i;var j=this.j;//设定退出条件if(j==3){//已经是最右边了return false;}//上面一个卡片var prev = cards[i][j+1];if(prev.num==0){//上一个卡片是空//移动,本质就是设置数字if(bool){//bool为true才执行,因为flase只是用来判断能否移动prev.num=this.num;this.num=0;//递归操作(注意这里是要 prev 来 move了)prev.moveRight(cards,bool);}return true;}else if(prev.num==this.num && !prev.merge){//合并操作(如果已经合并了,则不运行再次合并,针对当然轮)if(bool){bool为true才执行prev.merge=true;prev.num=this.num*2;this.num=0;}return true;}else {//上一个的num与当前num不同,无法移动,并退出return false;}
    }
    //向左移动
    Card.prototype.moveLeft=function(cards,bool) {var i=this.i;var j=this.j;//设定退出条件if(j==0){//已经是最左边了return false;}//上面一个卡片var prev = cards[i][j-1];if(prev.num==0){//上一个卡片是空//移动,本质就是设置数字if(bool){//bool为true才执行,因为flase只是用来判断能否移动prev.num=this.num;this.num=0;//递归操作(注意这里是要 prev 来 move了)prev.moveLeft(cards,bool);    }return true;}else if(prev.num==this.num && !prev.merge){//合并操作(如果已经合并了,则不运行再次合并,针对当然轮)if(bool){bool为true才执行prev.merge=true;prev.num=this.num*2;this.num=0;}return true;}else {//上一个的num与当前num不同,无法移动,并退出return false;}
    }
    

运行效果:

做到这里就基本完成了,加入其他一下辅助的东西就行了,比如重新开始、游戏胜利,游戏结束等,也就不多说了。

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。

代码获取方式:

订阅我的专栏《javascript精彩实例》后,可以查看专栏内所有的文章,并且联系博主免费获取你心仪的源代码,专栏的文章都是上过csdn热榜的,值得信赖,了解一下我的专栏!。**

热门专栏推荐

【1】Java小游戏(俄罗斯方块、飞机大战、植物大战僵尸等)
【2】JavaWeb项目实战(图书管理、在线考试、宿舍管理等)
【3】JavaScript精彩实例(飞机大战、贪吃蛇、验证码等)
【4】Java小白入门200例
【5】从零学Java、趣学Java
【6】Idea从零到精通

JavaScript实现2048小游戏,我终于赢了一把相关推荐

  1. javascript实现2048小游戏

    最近打算系统的学习一下javascript,想做点项目练手,就选择用javascript实现我比较喜欢玩的2048游戏,大体完成以后还是蛮有成就感的.(文末给出相关代码) 游戏相关截图:       ...

  2. 【javascript】2048小游戏

    目录 什么是2048 游戏状态机 游戏界面绘制 3.1 界面 3.2 数字的背景颜色 分数逻辑 4.1 加分 4.2 更新最高分 方向控制逻辑 5.1 数组 5.2 随机数 5.3 初始化 5.4 判 ...

  3. python游戏代码五子棋_用20行Python代码实现2048小游戏,你会吗?

    前些天在b站上看到有个大佬用c写了一个2048小游戏,我便一下来了兴趣.心想着,我貌似也能用Python来整一波,话不多说,直接开搞. 2048的游戏规则: 2048游戏总共有16个格子,初始时会有两 ...

  4. python秒表游戏代码_用20行Python代码实现2048小游戏,你会吗?

    前些天在b站上看到有个大佬用c写了一个2048小游戏,我便一下来了兴趣.心想着,我貌似也能用Python来整一波,话不多说,直接开搞. 2048的游戏规则: 2048游戏总共有16个格子,初始时会有两 ...

  5. html css js实现快递单打印_JS与HTML、CSS实现2048小游戏(六)

    在前面的五篇文章中,小编带大家完成了网页版2048小游戏的基本游戏逻辑.但是在游戏结束的gameover()方法中咱们只是简单的通过alert来弹出一些信息,这样的话只能出现下图的效果.这样的游戏,不 ...

  6. 2048小游戏设计思路

    2048小游戏设计思路: 游戏初始截图: 游戏过程中截图: 游戏失败截图: 部分代码: <!DOCTYPE html><html> <head><meta h ...

  7. 2048小游戏(原生js基础代码篇)

    今天在这里,我将给各位呈现一个简单的游戏代码. 想必大家都接触过2048这个小游戏吧,在悠闲时间用于消遣时间最好不过了,那么,我给大家写一个2048最基本的原生js代码. HTML: <!DOC ...

  8. html+css+js适合前端小白的实战全解(超详细)——2048小游戏(三)

    续上一小节,我们回到newgame()这个函数,我们之前只做了init()内函数,相当于一个初始化操作 现在,我们需要再随机两个两个生成数字. 随机生成数字在这个游戏里会经常出现,用户移动一步,也会产 ...

  9. html+css+js适合前端小白的实战全解(超详细)——2048小游戏(二)

    续上一小节,我们可以发现每一个grid-cell上的数字初始时并不在格子里. 这些数字可以随着玩家的操作而移动 ​ 我们可以想象:初始时每一个格子上的数为0,他们并不显示 ↓ 只有当grid-cell ...

最新文章

  1. R语言伯努利分布(Bernoulli distribution)函数(dbern, pbern, qbern rbern)实战
  2. CentOS5.5下NIS配置
  3. mysql分表方法-----MRG_MyISAM引擎分表法
  4. oom 如何避免 高并发_【面试题】如何设计一个高并发系统?
  5. tryLock尝试获取锁
  6. 计算机二级web题目(7.1)--综合选择题1
  7. 未来教育计算机二级书怎么样,未来教育计算机二级
  8. 前端框架:AntdUI 文档入门
  9. input上报流程分析【转】
  10. Does taro support react hook?
  11. MySQL 索引优化器选择索引的规则
  12. ROS学习笔记九:用C++编写ROS发布与订阅
  13. linux导出日志命令_Linux中的导出命令
  14. 亲身经历:2018年深圳保洁员工资时薪75,月薪不清楚
  15. mysql中timestamp字段
  16. 动态添加 data 属性的时候 发现某一个值无法取到
  17. 清华校友、香港科技大学准博士ICCV顶会论文被爆公然抄袭!去年CVPR也是抄的...
  18. 官宣!等你来赛,2022 第二届 STM32 Hackathon 挑战赛海选开放报名!
  19. 小程序map组件一——使用腾讯地图个性化地图组件、腾讯云可视化大屏展示
  20. HDU steps 1.2.6 Identity Card

热门文章

  1. 激光切割机雕刻机桌面打标机打码机点胶机写字机上位机C#源码STM32F407控制板源码
  2. 【Linux】Linux系统的常见指令
  3. SW toolbox国标生成斜齿轮失败
  4. HTC Desire (G7) VS MOTO Milestone VS MOTO XT800 个人对比评测
  5. java对数字证书的验证_JAVA对数字证书的常用操作
  6. java语言算法描述_六大java语言经典算法
  7. ENVI app store报错:打不开和显示无法联网
  8. 机器学习与深度学习——通过knn算法分类鸢尾花数据集iris求出错误率并进行可视化
  9. 用turtle画路飞
  10. Python周刊502期