代码改变世界

【整理】HTML5游戏开发学习笔记(1)- 骰子游戏

2014-10-14 14:25  Benoly  阅读(986)  评论(0编辑  收藏  举报

《HTML5游戏开发》,该书出版于2011年,似乎有些老,可对于我这样没有开发过游戏的人来说,却比较有吸引力,选择自己感兴趣的方向来学习html5,css3,相信会事半功倍。不过值得注意的是,该书的游戏是些小的游戏,内容相对比较基础,而且html5标准已经正式发布,可能会和书中所描述有少许出处。当然了,书中的小游戏还是比较不错的,适合我这样的前端开发不咋地的来练手,学习方式是在以自己的思路实现之后,再来看书中的实现思路,因为每个人有自己的开发习惯。

1.预备知识
在做第一个骰子游戏开发前,必须知道在html5中,如何绘制矩形和圆形

			/*获取2d绘图上下文			
			var ctx = document.getElementById('diceCanvas').getContext('2d')
			*/

			/*绘制矩形
			ctx.lineWidth = 1
			ctx.strokeStyle = '#000'
			ctx.strokeRect(100,50,100,100)

			ctx.fillStyle = '#f00'
			ctx.fillRect(100,50,100,100)
			*/

			/*绘制圆形或弧形.关闭路径,如果不关闭,这绘制的是不闭合的半圆
			ctx.lineWidth = 1
			ctx.strokeStyle = '#000'
			ctx.beginPath()
			ctx.arc(200,200,50,0,2*Math.PI*90/360,false)
			ctx.closePath() 
			ctx.stroke()
			*/

 


2.实现思路
设计和思考整个游戏中涉及的对象,包括骰子和游戏玩法。
为了简化在绘制每个圆点的时候对坐标的定位计算,我选择九宫格的布局,所以增加了个九宫格坐标对象

3.代码

/*九宫格坐标图*/
			function NineBlockBoxMap(squareSize){
				var size = squareSize/3,
						maps = [[],[],[]],
						x,y,
						n = 3

				for(x=0; x<n; x++){					
					for(y=0; y<n; y++){
						maps[x][y] = {x : x*size + size/2, y : y*size + size/2}
						//alert(maps[x][y].x+'|'+maps[x][y].y)
					}
				}

				this.maps = maps
			}

			NineBlockBoxMap.prototype.locate = function(x,y,offsetX,offsetY){
				var map = this.maps[x][y]
				return {x:map.x+offsetX,y:map.y+offsetY}
			}

			/*骰子*/
			function Dice(){

				var opts,
						ctx,
						maps

				function clearCanvas(){
					//WARNNING : 不能清除整个Canvas画布,而是要清除当前骰子尺寸的部分画布,因为整个画布有多个骰子
					//ctx.clearRect(0,0,opts.canvasWidth,opts.canvasHeight)
					ctx.clearRect(opts.diceX,opts.diceY,opts.diceWidth+2*opts.diceLineWidth,opts.diceHeight+2*opts.diceLineWidth)
				}

				function dotRandom(){
					return Math.floor(Math.random()*6+1)
				}
				
				function drawDots(num){		
					
					var doDraw = function(points){
						var point,i
						
						for(i=0; i<points.length; i++){
							point = points[i]
							
							ctx.beginPath()
							ctx.arc(point.x,point.y,opts.dotRadius,0,Math.PI*2,false)
							ctx.closePath()
							ctx.fill()
						}
					}
					
					var points = []
					
					//根据1-6的骰子点数在九宫格图中绘制1-6个圆点
					switch(num){
						case 1 :
						  points.push(maps.locate(1,1,opts.diceX,opts.diceY))		
							break
						case 2 :
							points.push(maps.locate(0,1,opts.diceX,opts.diceY))
							points.push(maps.locate(2,1,opts.diceX,opts.diceY))
							break
						case 3 :
							points.push(maps.locate(2,0,opts.diceX,opts.diceY))
							points.push(maps.locate(1,1,opts.diceX,opts.diceY))
							points.push(maps.locate(0,2,opts.diceX,opts.diceY))
							break
						case 4 :
							points.push(maps.locate(0,0,opts.diceX,opts.diceY))
							points.push(maps.locate(0,2,opts.diceX,opts.diceY))
							points.push(maps.locate(2,0,opts.diceX,opts.diceY))
							points.push(maps.locate(2,2,opts.diceX,opts.diceY))									
							break
						case 5 :
							points.push(maps.locate(0,0,opts.diceX,opts.diceY))
							points.push(maps.locate(0,2,opts.diceX,opts.diceY))
							points.push(maps.locate(1,1,opts.diceX,opts.diceY))
							points.push(maps.locate(2,0,opts.diceX,opts.diceY))
							points.push(maps.locate(2,2,opts.diceX,opts.diceY))			
							break
						case 6 :
							points.push(maps.locate(0,0,opts.diceX,opts.diceY))
							points.push(maps.locate(0,2,opts.diceX,opts.diceY))
							points.push(maps.locate(0,1,opts.diceX,opts.diceY))
							points.push(maps.locate(2,1,opts.diceX,opts.diceY))
							points.push(maps.locate(2,0,opts.diceX,opts.diceY))
							points.push(maps.locate(2,2,opts.diceX,opts.diceY))
							break
						default : 
							alert(num + ' num must be 1~6')
							break
					}

					ctx.fillStyle = opts.dotFillStyle
					doDraw(points)
				}

				function drawDice(){				
					//绘制骰子矩形				
					ctx.lineWidth = opts.diceLineWidth
					ctx.strokeStyle = opts.diceStrokeStyle
					ctx.strokeRect(opts.diceX,opts.diceY,opts.diceWidth,opts.diceHeight)

					//绘制骰子上的圆点
					var num = dotRandom()
					drawDots(num)

					return num
				}

				function extend(opt1,opt2){
					var name

					opt1 = opt1||{}
					opt2 = opt2||{}

					for(name in opt2){
						opt1[name] = opt1[name]||opt2[name]
					}

					return opt1	
				}

				return {

					init : function(options){
						opts = extend(options,{
							canvasId : 'diceCanvas',
							canvasWidth : 400,
							canvasHeight : 400,
							diceWidth : 90,
							diceHeight : 90,
							diceX : 50,
							diceY : 50,
							diceLineWidth : 5,
							diceStrokeStyle : '#000',
							dotFillStyle : '#000',
							dotRadius : 10							
						})

						var canvas = document.getElementById(opts.canvasId)

						if(canvas==null||canvas.tagName !== 'CANVAS'){
							alert('element must be canvas tag.')
							return
						}


						ctx = canvas.getContext('2d')
						maps = new NineBlockBoxMap(opts.diceWidth)

						return this
					},

					throwDice : function(onThrow){		
						//清除当前骰子的绘图区域
						clearCanvas()

						var num = drawDice()
						
						if(typeof onThrow === 'function'){
							onThrow(num)
						}

						return this
					}

				}
			}//end-Dice

			/*游戏规则
				2个骰子之和(2-12)
				第一次(7,11) 胜
				第一次(2,3,12 )输
				第一次 (4,5,6,8,9,10),继续 (第二次(7)输,如果正好和之前相同的点数则 胜,否则继续)
			*/
			var GameResult = {Lose:-1,Unknown:0,Win:1}

			var rule = function GameRule(){				
				var throwCount = 0
				var continual = false
				var previousSum = 0

				return {

					getResult : function(sum){
						var result = GameResult.Unknown,
								sum = parseInt(sum)

						throwCount++

						if( throwCount==1 ){
							if( sum==7||sum==11 ){
								result = GameResult.Win
							}
							else if( sum==2||sum==3||sum==12 ){
								result = GameResult.Lose
							}
						}
						else if(throwCount>1 && continual){
							if(throwCount==2&&sum==7){
								result = GameResult.Lose
							}
							else if(sum==previousSum){
								result = GameResult.Win
							}
						}

						previousSum = sum
						continual = ( result==GameResult.Unknown )
						
						return result
					},

					reset : function(result){
						if(result==GameResult.win||result==GameResult.Lose){
							throwCount = 0
							continual = false
							previousSum = 0
						}
					}

				}
			}()	
		
			/*app*/
			var throwDiceButton = document.getElementById('throwDiceButton')
			var dice1TextBox = document.getElementById('dice1TextBox')
			var dice2TextBox = document.getElementById('dice2TextBox')
			var resultTextBox = document.getElementById('resultTextBox')

			var dice1 = new Dice().init()
			var dice2 =new Dice().init({diceX : 200,diceY : 50,})

			throwDiceButton.onclick = function(){

				var result = GameResult.Unknown
						sum = 0

				//投掷骰子1
				dice1.throwDice(function(num){
					dice1TextBox.value = num
					sum += num
				})

				//投掷骰子2
				dice2.throwDice(function(num){
					dice2TextBox.value = num
					sum += num
				})

				result = rule.getResult(sum)
				rule.reset(result)

				if(result==GameResult.Win){
					resultTextBox.value = 'You Win : ' + sum
				}
				else if(result==GameResult.Lose){
					resultTextBox.value = 'You Lose : ' + sum
				}
				else{
					resultTextBox.value = 'Play again : ' + sum
				}				
			}

 


4.优化和完善
精简代码和抽象,增加记录得分,初始一定金额等效功能,使其更像是个赌博游戏。