代码改变世界

【整理】HTML5游戏开发学习笔记(4)- 记忆力游戏

2014-10-28 09:38  Benoly  阅读(289)  评论(0编辑  收藏  举报

1.预备知识
(1)Canvas绘制多边形
(2)Canvas绘制文字

2.实现思路
涉及的对象
  (1)场景Scene
  场景代表了画布上的一块区域,场景里的每个物体都是场景里的一个元素,其绘制统一由场景来调用绘制
 (2) 扑克牌Card
  包括翻开,关闭,移除等操作
 (3)一副扑克牌Deck
  包括洗牌
 (4)游戏玩法PlayingRule
 每次选择2张扑克进行比较,相等则消除(移除),不相等,则进行下一次的2张牌的选择 ,在进行比较

3.主要代码

			/*场景*/
			function Scene(canvasId){
				var canvas = document.getElementById(canvasId)
				var ctx = canvas.getContext('2d')
				var width = canvas.width
				var height = canvas.height
				
				//场景里所有的元素
				var elements = []

				function initEvents(){
					canvas.addEventListener('click',function(e){
						for(var i=0;i<elements.length;i++){
							if( typeof elements[i].onEvent == 'function' ){
								elements[i].onEvent(e)
							}
						}									
					},false)
				}

				return {

					addElement : function(element){
						elements.push(element)
					},

					init : function(){

						for(var i=0;i<elements.length;i++){
							if( typeof elements[i].init == 'function' ){
								elements[i].init(ctx)
							}
						}

						initEvents()
					}

				}
			}

			/*
				扑克牌
				德国扑克牌(一付55/54/32张)规格:9x5.7cm 8.7x5.7cm
			*/
			function Card(x,y,value,index){
				this.x = x
				this.y = y
				this.value = value				
				this.index = index
				this.status = this.StatusType.Closed

				this.ctx = null
			}

			Card.prototype = {

				constructor : Card,
				width : 60,
				height : 90,
				ratio : 1,
				StatusType : {Closed:0,Opened:1,Removed:2},
				timeout : 300,

				init : function(ctx){			
					this.ctx = ctx
					this.draw()
				},

				draw : function(){
					var ctx = this.ctx
					ctx.clearRect(this.x,this.y,this.width,this.height)

					switch(this.status){
						case 2 :
							ctx.fillStyle = '#ccc'
							ctx.fillRect(this.x,this.y,this.width*this.ratio,this.height*this.ratio)
							break						
							break
						case 1 :
							ctx.fillStyle = '#36f'
							ctx.fillRect(this.x,this.y,this.width*this.ratio,this.height*this.ratio)						
							//添加文字
							ctx.fillStyle = '#fff'
							ctx.font = 'bold 50px Verdana'
							ctx.fillText(this.value,this.x+this.width*.5-17,this.y+this.height*.5+17)
							break
						case 0 :
						default :
							ctx.fillStyle = '#f60'
							ctx.fillRect(this.x,this.y,this.width*this.ratio,this.height*this.ratio)
							break
					}
				},

				onEvent : function(e,cardPicked){
					var x = e.offsetX,
							y = e.offsetY

					//牌被选中,显示该牌的value值
					if(this.x<=x&&x<=this.x+this.width&&this.y<=y&&y<=this.y+this.height){
						this.open()//翻开扑克

						var self = this
						cardPicked(self)

						setTimeout(function(){
							self.close()//关闭扑克牌
						},self.timeout)
						
					}

					cardPicked(null)
				},

				open : function(){
					if(this.status == this.StatusType.Closed){					
						this.status = this.StatusType.Opened
						this.draw()
					}
				},

				close : function(){
					if(this.status == this.StatusType.Opened){
						this.status = this.StatusType.Closed
						this.draw()
					}						
				},

				remove : function(){
					this.status = this.StatusType.Removed
					this.draw()
				}

			}			

			/*整付扑克牌 52张*/
			function Deck(){
				this.cards = []
				this.rule = new PlayingRule()
			}

			Deck.prototype = {
				constructor : Deck,
				opts : {},
				//cards : 52,
				margin : 10,//牌之间的间距

				init : function(ctx){
					this.opts.ctx = ctx

					this.draw()
					this.shuffle()
				},				

				draw : function(){
					var ctx = this.opts.ctx
					var x = this.margin,
							y = this.margin,
							i = 3,
							index = -1

					for(i=3;i<9;i++){
						var card1 = new Card(x,y,i,++index)
						var card2 = new Card(x,y+card1.height+this.margin,i,++index)

						card1.init(ctx)
						card2.init(ctx)

						//console.log(card1.x+','+card1.y)
						//console.log(card2.x+','+card2.y)

						this.cards.push(card1)
						this.cards.push(card2)

						x += card1.width+this.margin
						y = y
					}
				},

				//洗牌
				shuffle : function(){
					var length = this.cards.length
					var r1
					var r2
					var i
					var value

					for(i=0;i<length;i++){
						r1 = Math.floor(Math.random()*length)
						r2 = Math.floor(Math.random()*length)

						value = this.cards[r1].value
						this.cards[r1].value = this.cards[r2].value
						this.cards[r2].value = value
					}

					/*debug*/
					for(i=0;i<length;i++){
						console.log(this.cards[i].index+' : '+this.cards[i].value)
					}
				},

				onEvent : function(e){
					var cards = this.cards
					var length = cards.length
					var i
					var card
					var self = this

					for(i=0;i<length;i++){
						card = cards[i]

						if(typeof card.onEvent == 'function'){							
							card.onEvent(e,function(card){
								if(card==null||card.status==card.StatusType.Removed){
									return
								}


								//有扑克牌被选中
								var isMatch = self.rule.pushCard(card).isMatch()
								var cards = self.rule.getCards()		
								var length = cards.length	

								if(isMatch){
									for(var i=0;i<length;i++){
										var card = cards[i]
										alert(card.index)
										card.remove()
									}
								}
								else{
									//如果不匹配,当前选中的扑克牌会自动定时关闭
									//DONOTHING	
								}

								//如果有2张扑克,则清除玩法中的选中的2张扑克牌
								self.rule.refresh()								
							})
						}
					}
				}

			}

			/*玩法规则*/
			function PlayingRule(){
				var cards = []

				return {
					
					pushCard : function(card){
						if(cards.length<2){
							cards.push(card)

							console.log('push:'+card.value)
						}

						return this
					},

					isMatch : function(){
						if(cards.length==2){
							return cards[0].value === cards[1].value
						}

						return false
					},

					refresh : function(){
						if(cards.length==2){
							cards.length = 0
						}
					},


					getCards : function(){
						return cards;
					}

				}
			}

			//app
			window.onload = function(){		

				var scene = new Scene('canvas1')
				var deck = new Deck()

				scene.addElement(deck)
				scene.init()

			}

 



4.优化和完善
(1)有bug,可以作弊,选择的拥有比较的2张牌可以为同一张,需添加判断
(2)简化了书中的例子,图形图片绘制改为了数字文字的现实
(3)可以进行图形绘制,使扑克牌逼真些
(4)考虑自适应大小的设置,如扑克牌的比例随着手机屏幕大小自适应