原生JS实战:写了个斗牛游戏,分享给大家一起玩!
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5869953.html
该程序是本人的个人作品,写的不好,未经本人允许,请不要用于其它用途!
奋战一天一夜终于把斗牛游戏给写出来了(主要是除bug时间用的多!若大家发现新的bug欢迎留言)
这是游戏规则:百度牛牛规则
点击查看演示:
苏福的JS小游戏
无牛
1000
牛牛
1000
牛9
3
3
5
10
随机
1000
牛8
1000
请押注
开始游戏
下一局
我是新手,代码写的有点乱,计算逻辑有点复杂,但最终还是实现了游戏效果!真的好开心,也深刻体会到一点就是,敲代码的时候注意力一定要十分集中,不然后期除bug真的很头痛!其实大部分bug都是一些小错误引起的!
作为新生之一,多写点代码,是最能提高自己的能力的!不管要写什么,写多大的程序,把想法、规则、流程先写出来,然后再敲代码,这样才不会乱!
多写代码的最大好处就是可以熟练API的使用,本人还没学任何第三方类库呢,连JQ都没学过,到现在还一直撸原生,暂时是ES5,过段时间再练习ES6,据说,学好原生,再学别的都很容易上手,我一直很相信这句话,就是不知道靠不靠谱,求前辈们指点迷津!很烦恼的一个问题:选NG呢?还是react呢?
正题开始
HTML、CSS的代码都是用的比较基础的知识,大家应该都能看懂,就不多废话了;
JS代码部分:
根据游戏规则(这里我只写了支持4个玩家),比较核心的有几个:创建一副扑克牌、创建玩家、计算出牛几
- 创建一副扑克牌,我定义了
Game
类,下面是代码+注释
//=================Game类
function Game() {
this.systemCards = Game.createCards();
}
//静态方法createCards创建一副牌并返回乱序后的牌
Game.createCards = function(){
var cards = [],
cardType = [1, 2, 3, 4], //牌的花色,为了便于比较不同花色的大小,采用数值代替黑桃、红桃等文字
cardPoint = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; //牌面值,J、Q、K分别为11,12,13,这里没有大小王
cardPoint.forEach(function (p) { //两个forEach循环为每个花色创建一组牌,共四组,合计52张牌
cardType.forEach(function (t) {
var card = {};
card[t] = p;
cards.push(card)
})
});
return this.disorder(cards);
};
//乱序方法,互相替换位置i的元素和随机位置的元素(中间变量temp保存值),这是很基础的用法,必须掌握
Game.disorder = function (cards){
var temp,len = cards.length;
for(var i = 0; i < len; i++){
var r = getRandom(0,len);
temp = cards[i];
cards[i] = cards[r];
cards[r] = temp;
}
return cards;
};
- 创建玩家类
Player
//=================Player类
var Player = function () {
this.banker = false; //是否为庄家
this.money = 1000;
};
//返回一副牌中的前5张牌,后期用于发牌给玩家
Player.prototype.getCards = function (g) {
g.systemCards = g.systemCards.length < 20 ? Game.createCards() : g.systemCards;//牌不够发时重新拿一副牌,4个人*5=20
return g.systemCards.splice(0,5);
};
- 计算出牛几:这个问题我想了很久,感觉有点复杂,其实真正动手去写的时候感觉不是太难!(被这么点小学算法难倒了,还怎么在这个圈混啊!)
//计算核心函数`sufuCaculate`,这里扩展了`Array`类,只是为了方便调用,前提是得取个个性一点的名字,不然哪一天官方也出个同名的内置方法,你的项目就悲催了!
Array.prototype.sufuCaculate = function () {
if((!this)||this.length<5){throw new Error('sufuCaculate()参数错误')}
var card5 = this, before10Count = 0, after10Count = 0, maxSameCount = getMaxSameCount(this);
if(maxSameCount == 4){return 14} //4张相同的,返回'炸弹'
if(this.every(function (x) {return x<5;})){return 13}//5张牌都小于5,返回'五小'
if(this.every(function (x) {return x>10;})){return 12}//5张牌全为花,返回'五花'
turnTrueValue(card5);//把大于10的牌变成10,并计算等于10的牌的数量
if(before10Count == 1 && after10Count == 5){return 11}//5张牌中一张为10,另外4张为花,返回'四花'
//用了三层的for循环才实现了,求前辈们指点好一点的逻辑!
for(var i = 0; i<3; i++){
for(var j = i+1;j<4;j++){
for(var k = j+1;k<5;k++){
if(sum([card5[i],card5[j],card5[k]]) == 0){
var copy = card5.slice();
delete copy[i];
delete copy[j];
delete copy[k];
var a = sum(copy.filter(function(){return true}));
if(a == 0){return 10}//牛牛
else{return a}//a牛
}
}
}
}
return 0;//'无牛'
//求和并求于10
function sum(arr){
return arr.reduce(function(a,b){return a+b})%10
}
//获得最大相同牌数
function getMaxSameCount(card5){
var count = 1, sameCardCount = 1;
for(var i = 0; i<5; i++){
for(var j = 0; j<5;j++){
if(j == i){continue}
if(card5.indexOf(card5[i],j)>0){
count++;
}
}
sameCardCount = Math.max(count,sameCardCount);
count = 1;
}
return sameCardCount;
}
//把大于10的牌全部转为10
function turnTrueValue(card5){
for(var i = 0; i<5; i++){
if(card5[i] === 10){before10Count++}
card5[i] = card5[i]>=10 ? 10 : card5[i];
if(card5[i] === 10){after10Count++}
}
}
};
接下来,怎么写呢?本人是这样的,先从程序入口出发,一步一步往前推,写到感觉会被重复用到的代码就拿出来,放进一个单独的工具函数,方便重复使用,可以大大地减少代码量!注意工具函数放的位置,如果它只被用在某函数内部,且依赖该函数的变量的话就毫不客气的放在这个函数里面吧,省的传参等的麻烦,要是它会被大于2个函数使用,就放在外面吧,把参数设置成通用的。
游戏入口函数start()
,游戏要开始,得有个按钮按吧,好吧来个按钮绑定事件beginBtn.onclick
,点击后进入游戏界面(显示基本元素)
//点击开始按钮执行
beginBtn.onclick = function () {
var str = prompt('请输入游戏名字','玩家');
player3Name.innerHTML = (str||'玩家')+' 金钱:';
begin();
firstBanker();
showOrHide('none',beginBtn);
showOrHide('block',player1Money,player2Money,player3Money,player4Money,player3Bg);
showOrHide.bind(null,'block').apply(null,playerCards); //这是我为了偷懒,突发奇想写出来的,居然能用,呵呵了!如果看不懂,就去苏福的博客园看bind,apply的文章
showOrHide.bind(null,'block').apply(null,playerNameMoneys);
runing();
};
开始后得有玩家吧,好吧,创建4个玩家,放进一个数组里面,方便访问
//创建四个玩家
function begin(){
for(var i = 0; i<4 ; i++){
players.push(new Player());
}
}
第一轮先随机确定庄家firstBanker()
,不介绍了,自己看源码。
然后呢,得来个倒计时函数 runTime(msg,t)
,这个函数比较通用,在start()范围内会被重复使用,设计如下:
//倒计时函数,看不懂?多翻翻我的博客,有相关知识介绍的文章
function runTime(msg,t){
t--;
if(t<0){clearTimeout(timeId);timeId=null;return}
info.innerHTML = msg;
timeInfo.innerHTML = t;
timeId = setTimeout(function () {
runTime(msg,t);
},1000);
}
在点击开始按钮时,会启动游戏的流程,事件回调函数内调用runing()
function runing(){
runTime('请押注',4);
callIn(); //押注函数
waiting(deal,5);//等待delay时间到时,执行deal发牌方法
}
是的,需要个押注函数callIn()
,补贴代码了,比较简单,电脑玩家给个随机数,主角通过按钮来取值,而庄家不取值(其它代码就是界面元素的显示与隐藏)
接下来就是deal()
方法,发牌方法,取出玩家的手牌的数据,把花色值、牌面值、牛几分别存进单独的数组,以便使用。
function deal() {
for(var i = 0; i<4 ; i++){
cs[i] = players[i].getCards(g);
keys[i] = getKeyValue(cs[i]).keys;
values[i] = getKeyValue(cs[i]).values;
results[i] = getCow(values[i])
}
showOrHide('none',player3InBtn);
setCard('set',keys,values,player1Card,player2Card,player3Card,player4Card);
runTime('计算结果',4);
waiting(function(){showResult(results)},5);
}
上面这个deal()
方法又需要调用显示或隐藏元素的showOrHide()
方法,看名字就知道啦,所以函数的命名很关键,不然代码长了,自己都不知道这是什么鬼了!showOrHide()
这个方法用的最多,所以必须写成通用的格式:
//改变元素的display属性,看不懂?多翻翻我的博客,有相关知识介绍的文章
function showOrHide(str){
var len = arguments.length;
for(var i=1;i<len;i++){
arguments[i].style.display = str;
}
}
这里先贴个工具函数:
//获得某一张牌的类型和值`getKeyValue()`,用来取牌的花色值和牌面值,内置的`Object.keys()`方法不支持IE9以下
var getKeyValue = function (cards){
var keys = [],values = [];
cards.forEach(function (n) {
var key = +Object.keys(n);
keys.push(key);
values.push(n[key]);
});
return {
keys:keys,
values:values
};
};
发牌函数里调用了setCard()
,显示或重置所有玩家的牌,我写的这个斗牛游戏的牌不是用的图片,而是用了CSS,下面的changeClassName()
就是用来应用样式的函数
//显示所有玩家的牌
function setCard(str,ks,vs){ //str='set' or 'reset'
var args = Array.prototype.slice.call(arguments,3),len = args.length;
for(var j=0;j<len;j++){
var n = args[j].id.charAt(6)-1;
for(var i = 0;i<5;i++){
if(str === 'reset'){
changeClassName(args[j].children[i],'card-'+ks[n][i],false);
args[j].children[i].innerHTML = '';
}else if(str === 'set'){
changeClassName(args[j].children[i],'card-'+ks[n][i],true);
args[j].children[i].innerHTML = vs[n][i];
}
}
}
}
玩家压完注后,得显示结果了,showResult()
,这里又用到了showOrHide()
、runTime()
、waiting()
//显示所有玩家的结果
function showResult(results){
var resultsStr = [];
for(var i=0;i<4;i++){
resultsStr.push(getResultInfo(results[i]));
}
showOrHide('block',result1,result2,result3,result4);
result1.innerHTML = resultsStr[0];
result2.innerHTML = resultsStr[1];
result3.innerHTML = resultsStr[2];
result4.innerHTML = resultsStr[3];
var str = pay(); //pay()就是付钱的意思啦
runTime(str,10);
waiting(function(){showOrHide('block',restartBtn);},11) //延时时间到后显示下一局按钮
}
第一局完了,要开始下一局了(以后的所有下一局都将一样),restartBtn.onclick = restart;
,好吧,定义个回调函数restart()
function restart(){
showOrHide('none',restartBtn);
resetBanker(); //根据上一局结果,根据游戏规则,重选庄家
setCard('reset',keys,values,player1Card,player2Card,player3Card,player4Card);
clearArrays(results,cs,keys,values,playerCallIns); //清楚上一局保存的所有数据
showOrHide('none',result1,result2,result3,result4);
runing(); //循环开始了!!!
}
就这样,游戏就可以一直玩下去了,一直可以点下一局!
这个游戏我定义了好多变量,用了好多ID,整了不少函数,感觉逻辑偏复杂了,虽然定义了Game和Player,有点面向对象了,但整体来说都是函数哦!请前辈们多指点指点在下啊!!!QQ: 553150580
原生JS实战:写了个斗牛游戏,分享给大家一起玩!的更多相关文章
- 原生JS实战:分享一个首页进度加载动画!
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5871134.html 该程序是本人的个人作品,写的不好,可以参考,但未经 ...
- 从零开始手把手教你使用原生JS+CSS3实现幸运水果机游戏
项目体验地址 免费视频教程 游戏介绍 幸运水果机是一款街机游戏,游戏界面由24个方格拼接成一个正方形,每个方格中都有一个不同的水果图形,方格下都有一个小灯.玩家使用游戏币选择希望押注的目标,按下开始后 ...
- 原生JS实战:写了个一边玩游戏,一边记JS的API的游戏
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5878913.html 本程序[一边玩游戏,一边记JS的API]是本人的个 ...
- 原生JS实战:经典贪吃蛇(开局10倍速度,来看看你最高能得多少分!)
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5875523.html 该程序是本人的个人作品,写的不好,未经本人允许,请 ...
- 用原生js来写一个swiper滑块插件
是不是有点印象了,没错,他的最基本的用法就是左右滑动,插件使用者只需要写几行简单的html和js即可实现一个简单滑动效果,不过你完全可以组合各种元素来适应不同的场景. 当然插件我已经写好了,咱 ...
- 【原生JS】写最简单的图片轮播
非常简单的一个大图轮播,通过将控制显示位置来进行轮播效果,写来给正在学习的新手朋友们参考交流. 先看效果:(实际效果没有这么快) 先看布局: <div id="display" ...
- 原生js来写获取元素距离顶部距离,以及滚动条滚动指定距离和时间控制
这是我在写vue项目里封装的一个公共js类 里面还有一些其他的方法,一并拿过来了 class Public { isDesktop(){ //判断是否为pc端 return (window.scree ...
- 用最少的JS代码写出贪吃蛇游戏---迷你版
游戏进行页面展示 GAME OVER 页面展示 代码如下: <!doctype html> <html> <body> <canvas id=&q ...
- 用原生JS写一个网页版的2048小游戏(兼容移动端)
这个游戏JS部分全都是用原生JS代码写的,加有少量的CSS3动画,并简单的兼容了一下移动端. 先看一下在线的demo:https://yuan-yiming.github.io/2048-online ...
随机推荐
- Execute SQL Task 参数和变量的映射
Execute SQL Task能够执行带参数的SQL查询语句或存储过程(SP),通过SSIS的变量(Variable)对参数赋值.对于不同的Connection Manager,在Task中需要使用 ...
- JAVA基础代码分享--DVD管理
问题描述 为某音像店开发一个迷你DVD管理器,最多可存6张DVD,实现碟片的管理. 管理器具备的功能主要有: 1.查看DVD信息. 菜单选择查看功能,展示DVD的信息. 2.新增DVD信息 选择新增功 ...
- Web Fundamentsals学习1-Multiple-Screen-Site
你的一个运行于多设备网站(Your First Multi-device Site) 遵循的步骤: 1.定义信息架构(information architecture)和页面结构(structure ...
- 窥探Swift系列博客说明及其Swift版本间更新
Swift到目前为止仍在更新,每次更新都会推陈出新,一些Swift旧版本中的东西在新Swift中并不适用,而且新版本的Swift会添加新的功能.到目前为止,Swift为2.1版本.去年翻译的Swift ...
- 如何用Python实现杨辉三角和心
1. 如何实现杨辉三角 import copy list=[] newlist=[] def Fibonacci(list,n): newlist.append(0) if n ==1: return ...
- [c++] Operator overloading
c++的操蛋属性:自己为一档,空一档,其他随意. UB_stack a; UB_stack b = a; // copy auto c = a; auto d {a}; // (or auto d = ...
- spring整合hibernate的详细步骤
Spring整合hibernate需要整合些什么? 由IOC容器来生成hibernate的sessionFactory. 让hibernate使用spring的声明式事务 整合步骤: 加入hibern ...
- struts2学习笔记--struts.xml配置文件详解
这一节主要讲解struts2里面的struts.xml的常用标签及作用: 解决乱码问题 <constant name="struts.i18n.encoding" value ...
- 利用GeoWebCache实现WebGIS地形图展示的缓存优化
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.前言 在WebGIS中,影像金字塔是一个很重要的概念.在WebGI ...
- C#中DataTable转化为List<T>解析
在.net项目中使用到DataTable和List<T>集合的地方较多, 泛型的好处: 它为使用c#语言编写面向对象程序增加了极大的效力和灵活性.不会强行对值类型进行装箱和拆箱,或对引用类 ...