今天写一写模拟类的算法,如果只是模拟一些答案,比如两个人对战掉多少血,最后谁赢谁输,没有什么乐趣,今天就用javascript来实现一个坦克大战的游戏,最后的界面如下:
Common.js
//一些公共函数 //获取ID var $ = function (id) { return typeof id === "string" ? document.getElementById(id) : id }; //获取tagName var $$ = function (tagName, oParent) { return (oParent || document).getElementsByTagName(tagName) }; //获取class var $$$ = function (sClass, oParent) { var aClass = [], i = 0, reClass = new RegExp("(\\s|^)" + sClass + "($|\\s)"), aElement = $$("*", oParent); for (i = 0; i < aElement.length; i++) reClass.test(aElement[i].className) && aClass.push(aElement[i]); return aClass }; Config.js var Resource = { ROOT: "images/", IMG_HOME: "home.bmp", IMG_TREE: "tree.bmp", IMG_WATER: "water.bmp", IMG_IRON: "iron.bmp", IMG_BLOCK: "block.bmp", IMG_SBLOCK: "smallblock.bmp", IMG_TANKS_NORTH: "tanks_north.bmp", IMG_TANKS_SOUTH: "tanks_south.bmp", IMG_TANKS_WEST: "tanks_west.bmp", IMG_TANKS_EAST: "tanks_east.bmp", IMG_TANKE_NORTH: "tanke_north.bmp", IMG_TANKE_SOUTH: "tanke_south.bmp", IMG_TANKE_WEST: "tanke_west.bmp", IMG_TANKE_EAST: "tanke_east.bmp", IMG_BULLET: "hitterg.bmp" }; var GameConfig = { MapWidth: 800,//地图宽度 MapHeight: 500,//地图高度 TanksSpeed: 3,//我方坦克移动速度 TankeSpeed: 3,//敌方坦克移动速度 BulletSpeed: 9//子弹速度 } //单位类型 var Type = { TankHuman: 0x1, //我方坦克 TankComputer: 0x2, //敌方坦克 BulletHuman: 0x4, //我方坦克炮弹 BulletComputer: 0x8, //敌方坦克炮弹 Home: 0x10,//老家 Block: 0x20,//钻块 All: 0xFFFF } |
BaseUnit.js
//向量操作 var Vector = new Array(); Vector.Cross = function (v1, v2) { return (v1.x * v2.y - v1.y * v2.x); } Vector.Length = function (v1) { return (Math.sqrt(v1.x * v2.x + v1.y * v2.y)); } function BaseUnit() { } //游戏单位 BaseUnit.prototype = { //判断两个物件或者区域是否相交,若相交,则要么顶点互相包含,要么矩形边界(或对角线)相交 Intersects : function (obj) { if (this.pointInside(obj.posx, obj.posy) || this.pointInside(obj.posx + obj.width * obj.unitWidth, obj.posy) || this.pointInside(obj.posx, obj.posy + obj.height * obj.unitHeight) || this.pointInside(obj.posx + obj.width * obj.unitWidth, obj.posy + obj.height * obj.unitHeight)) { return true; } else if (obj.pointInside(this.posx, this.posy) || obj.pointInside(this.posx + this.width * this.unitWidth, this.posy) || obj.pointInside(this.posx, this.posy + this.height * this.unitHeight) || obj.pointInside(this.posx + this.width * this.unitWidth, this.posy + this.height * this.unitHeight)) { return true; } else if (obj.posx != null && obj.posy != null)//判断矩形对角线相交 |v1 X v2||v1 X v3| < 0 { var vector1 = new Object(); var vector2 = new Object(); var vector3 = new Object(); vector1.x = this.width * this.unitWidth; vector1.y = this.height * this.unitHeight; //矩形对角线向量 vector2.x = obj.posx - this.posx; vector2.y == obj.posy - this.posy; vector3.x = vector2.x + obj.unitWidth * obj.width; vector3.y = vector2.y + obj.unitHeight * obj.Height; if ((Vector.Cross(vector1, vector2) * Vector.Cross(vector1, vector3)) < 0) { return true; } } return false; }, pointInside : function (x, y) //判断一个点是否在这个物件内部 { if (this.posx == null || this.posy == null) { return false; } if (x >= this.posx && x <= this.posx + this.width * this.unitWidth && y >= this.posy && y <= this.posy + this.height * this.unitHeight) { return true; } return false; }, getPosition : function () //返回物件的重心,因为是矩形所以返回中点 { var pos = new Array(); pos.push(this.posx + this.unitWidth * this.width / 2); pos.push(this.posy + this.unitHeight * this.height / 2); return pos; }, isVisible : function () { var obj = document.getElementById(this.id); return obj != null && obj.style.display != ‘none‘; }, Test : function (posx, posy) { this.oldpos = new Array(this.posx, this.posy); this.posx = posx; this.posy = posy; }, Recover : function () { if (this.oldpos != null) { this.posx = this.oldpos[0]; this.posy = this.oldpos[1]; return true; } return false; } } |
Regiong.js
//地图区域类 function Region(id, width, height, type) { Region.metaData[id] = this; Region.metaData.All.push(this); this.type = type; this.width = width; this.height = height; this.id = id; Region.prototype._parseType = function () { this.imageUrl = Region.mapImages[this.type]; switch (type) { case "tree": this.allowPassType = 0; //规定允许何种类型的Unit通过,0表示不允许任何单位通过 this.absorbType = Type.BulletHuman | Type.BulletComputer; //吸收(使消失)何种类型的Unit this.unitWidth = 16; this.unitHeight = 16; break; case "water": this.allowPassType = Type.BulletHuman | Type.BulletComputer; //规定允许何种类型的Unit通过,0表示不允许任何单位通过 this.absorbType = 0; this.unitWidth = 16; this.unitHeight = 16; break; case "iron": this.allowPassType = 0; //规定允许何种类型的Unit通过,0表示不允许任何单位通过 this.absorbType = Type.BulletHuman | Type.BulletComputer; //吸收(使消失)何种类型的Unit; this.unitWidth = 16; this.unitHeight = 16; break; default: this.allowPass = 0xFF; break; } } Region.prototype.Draw = function (posx, posy) { this.posx = posx; this.posy = posy; var backCanvs = $("_back"); var newRegion = $(this.id); if (newRegion == null) { newRegion = document.createElement("span"); newRegion.id = this.id; backCanvs.appendChild(newRegion); } newRegion.style.position = "absolute"; newRegion.style.left = this.posx; newRegion.style.top = this.posy; newRegion.style.width = this.width * this.unitWidth; newRegion.style.height = this.height * this.unitHeight; newRegion.style.backgroundImage = "url(" + this.imageUrl + ")"; } Region.prototype.Overlaped = function () //判断是否和其他区域重叠 { if (this.posx == null || this.posy == null) { return false; } for (var i = 0; i < Region.metaData.All.length; i++) { if (this == Region.metaData.All[i] || Region.metaData.All[i].posx == null || Region.metaData.All[i].posy == null) { continue; } if (this.Intersects(Region.metaData.All[i])) { return true; } } return false; } Region.prototype.Dispose = function () { var backCanvs = $("_back"); var unit = $(this.id); backCanvs.removeChild(unit); Region.metaData[this.id] = null; for (var i = 0; i < Region.metaData.All.length; i++) { if (Region.metaData.All[i].id == this.id) { Region.metaData.All.splice(i, 1); } } this.Disposed = true; } this._parseType(); } Region.prototype = new BaseUnit(); Region.mapImages = new Array();//保存图片路劲 Region.mapImages["tree"] = Resource.ROOT + Resource.IMG_TREE; Region.mapImages["water"] = Resource.ROOT + Resource.IMG_WATER; Region.mapImages["iron"] = Resource.ROOT + Resource.IMG_IRON; Region.mapImages["block"] = Resource.ROOT + Resource.IMG_BLOCK; Region.metaData = new Array(); Region.metaData["RandomRegions"] = new Array(); Region.metaData.All = new Array(); |
KeyCodeList.js//键盘点击类
var KeyCodeList = new Array(); KeyCodeList.KeysDown = new Array(); KeyCodeList.KeysUp = new Array(); KeyCodeList.KeysDown.Put = function (keyCode) { var retVal = KeyCodeList.KeysDown.Remove(keyCode); KeyCodeList.KeysDown.push(keyCode); return retVal; } KeyCodeList.KeysDown.Remove = function (keyCode) { for (var i = 0; i < KeyCodeList.KeysDown.length; i++) { if (KeyCodeList.KeysDown[i] == keyCode) { KeyCodeList.KeysDown.splice(i, 1); return true; } } return false; } KeyCodeList.KeysDown.getKey = function () { if (KeyCodeList.KeysDown.length <= 0) { return null; } else { return KeyCodeList.KeysDown[KeyCodeList.KeysDown.length - 1]; } } AIAction.js//敌方坦克动作类
var AIAction = new Array(); AIAction.takeAction = function (style, obj, seed) { self.status = seed; switch (style) { case "normal": var choose = Math.floor(seed * Math.random()); if (choose > 0 && choose <= 1) { obj.direction = "_east"; obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_EAST; obj.unitWidth = 31; obj.unitHeight = 26; } else if (choose <= 2) { obj.direction = "_west"; obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_WEST; obj.unitWidth = 31; obj.unitHeight = 26; } else if (choose <= 3) { obj.direction = "_north"; obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_NORTH; obj.unitWidth = 26; obj.unitHeight = 31; } else if (choose <= 4) { obj.direction = "_south"; obj.imageUrl = Resource.ROOT + Resource.IMG_TANKE_SOUTH; obj.unitWidth = 26; obj.unitHeight = 31; } if (choose >= 30 && choose <= 40) { obj.tankFire("BulletComputer", 3); } break; default: break; } } GameUnit.js
function GameUnit(id, width, height, type) //游戏元素 —— 摆放在地图区域上的物件 { GameUnit.metaData[id] = this; GameUnit.metaData.All.push(this); this.id = id; this.width = width; this.height = height; this.type = type; this.initialized = false; this.Disposed = false; this._time = new Date().getTime(); //活动时间,被一些物件使用,例如判断连续射击等等 GameUnit.prototype._parseType = function () //初始化物件 { this.imageUrl = GameUnit.mapImages[this.type]; switch (type) { case "Home": this.allowPassType = 0; this.absorbType = Type.BulletHuman | Type.BulletComputer; this.unitWidth = 39; this.unitHeight = 33; this.direction = "_north"; this.AIMode = "normal"; break; case "TankHuman": this.allowPassType = 0; this.absorbType = Type.BulletComputer | Type.BulletHuman; this.unitWidth = 29; this.unitHeight = 32; document.body.eventHandler = this; document.body.onkeydown = function () { this.eventHandler.KeyDown(this, event); }; document.body.onkeyup = function () { this.eventHandler.KeyUp(this, event); }; this.direction = "_north"; break; case "TankComputer": this.allowPassType = 0; this.absorbType = Type.BulletHuman | Type.BulletComputer; this.unitWidth = 26; this.unitHeight = 31; this.direction = "_south"; this.AIMode = "normal"; break; case "BulletHuman": this.allowPassType = 0; this.absorbType = Type.BulletComputer | Type.BulletHuman | Type.TankComputer | Type.Home | Type.Block; this.unitWidth = 12; this.unitHeight = 12; this.repeatable = "norepeat"; this.autoDispose = true; break; case "BulletComputer": this.allowPassType = 0; this.absorbType = Type.BulletComputer | Type.BulletHuman | Type.TankHuman | Type.Home | Type.Block; this.unitWidth = 12; this.unitHeight = 12; this.repeatable = "norepeat"; this.autoDispose = true; break; case "Block": this.allowPassType = 0; //规定允许何种类型的Unit通过,0表示不允许任何单位通过 this.absorbType = Type.BulletHuman | Type.BulletComputer; //吸收(使消失)何种类型的Unit; this.unitWidth = 16; this.unitHeight = 16; break; default: break; } } GameUnit.prototype.Kill = function (obj) //销毁一个物件 { obj.clearInterval(); obj.type = 0; obj.dead = true; obj.allowPassType = Type.All; obj.absorbType = 0; obj.KeyDown = obj.KeyUp = function () { }; obj.AIMode = 0; obj.Draw(0, 0); obj.whenDead(); } this.whenDead = function () { }; //物件被销毁后触发的事件,可根据对象重写 GameUnit.prototype.KeyUp = function (sender, event) { if (event.keyCode < 37 || event.keyCode > 40) { if (event.keyCode == 32) { clearInterval(this.shooterTimer); this.shooterTimer = null; } return false; } else //处理方向键 { KeyCodeList.KeysDown.Remove(event.keyCode); if (KeyCodeList.KeysDown.getKey() == null) { this.clearInterval(); } } } GameUnit.prototype.KeyDown = function (sender, event) { if (event.keyCode >= 37 && event.keyCode <= 40) //处理方向键 { if (KeyCodeList.KeysDown.getKey() == event.keyCode) { return; } KeyCodeList.KeysDown.Put(event.keyCode); this.UnitSpeed = GameConfig.TanksSpeed; if (event.keyCode == 37) //left { this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_WEST; this.unitWidth = 32; this.unitHeight = 29; this.direction = "_west"; this.ready(3); } else if (event.keyCode == 38) //up { this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_NORTH; this.unitWidth = 29; this.unitHeight = 32; this.direction = "_north"; this.ready(3); } else if (event.keyCode == 39) //right { this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_EAST; tmp = this.unitWidth; this.unitWidth = 32; this.unitHeight = 29; this.direction = "_east"; this.ready(3); } else if (event.keyCode == 40) //down { this.imageUrl = Resource.ROOT + Resource.IMG_TANKS_SOUTH; this.unitWidth = 29; this.unitHeight = 32; this.direction = "_south"; this.ready(3); } } if (event.keyCode == 32) //space -- shoot! { if (this.shooterTimer == null) { this.shooterTimer = setInterval("GameUnit.metaData." + this.id + ".humanFire()", 1); } } } GameUnit.prototype.tankFire = function (type, speed) { if (speed == null) { speed = GameConfig.BulletSpeed; } var shootTime = new Date().getTime(); //if (GameUnit.metaData["bullet" + this.id] == null || GameUnit.metaData["bullet" + this.id].dead) if (shootTime - this._time >= 500) { this._time = shootTime; var bullet = new GameUnit("bullet" + shootTime, 1, 1, type); var pos = this.getPosition(); var x = pos[0] - bullet.unitWidth / 2; var y = pos[1] - bullet.unitHeight / 2; bullet.direction = this.direction; if (this.direction == "_north") { y -= this.unitHeight / 2 + 10; } if (this.direction == "_south") { y += this.unitHeight / 2 + 10; } if (this.direction == "_east") { x += this.unitWidth / 2 + 10; } if (this.direction == "_west") { x -= this.unitWidth / 2 + 10; } bullet.Draw(x, y); bullet.ready(speed); return; } else { return; } } GameUnit.prototype.humanFire = function () { this.tankFire("BulletHuman"); } GameUnit.prototype.setInterval = this.readyMove; GameUnit.prototype.clearInterval = function () { clearInterval(this.timer); this.timer = null; this.setInterval = this.readyMove; } GameUnit.prototype.readyMove = function (func) { if (this.timer == null) this.timer = setInterval("GameUnit.metaData." + this.id + "." + func, 10); this.setInterval = function () { }; } GameUnit.prototype.ready = function (speed) { if (this.timer == null) { this.UnitSpeed = speed; this.setInterval("Move();"); } } GameUnit.prototype.Move = function () { _AI_seed = 300; if (this.direction == null) { throw new Error("This object cannot move!"); } this.Test(this.posx, this.posy); switch (this.direction) { case "_west": if (this.posx > 5) this.posx -= this.UnitSpeed; else { _AI_seed = 100; if (this.autoDispose) this.Dispose(); } break; case "_north": if (this.posy > 5) this.posy -= this.UnitSpeed; else { _AI_seed = 100; if (this.autoDispose) this.Dispose(); } break; case "_east": if (this.posx < GameConfig.MapWidth - this.unitWidth * this.width) this.posx += this.UnitSpeed; else { _AI_seed = 100; if (this.autoDispose) this.Dispose(); } break; case "_south": if (this.posy < GameConfig.MapHeight - this.unitHeight * this.height) this.posy += this.UnitSpeed; else { _AI_seed = 100; if (this.autoDispose) this.Dispose(); } break; default: throw new Error("Error direction to move!"); } var region = this.Region(); if (this.meetDeal(region)) { _AI_seed = 100; } var unit = this.Unit(); if (this.meetDeal(unit)) { _AI_seed = 100; } this.Draw(this.posx, this.posy); if (this.AIMode != null) { AIAction.takeAction(this.AIMode, this, _AI_seed); } } GameUnit.prototype.meetDeal = function (obj) //和物件碰撞后执行的动作 { var killObj = false; if (obj == null || obj.Disposed) { return false; } if ((this.absorbType & Type[obj.type]) != 0) { killObj = true; } if ((obj.absorbType & Type[this.type]) != 0) { if (killObj) { this.Kill(obj); } this.Kill(this); return true; } if ((obj.allowPassType & Type[this.type]) == 0) { this.Recover(); return true; } } GameUnit.prototype.Draw = function (posx, posy) { if (this.Disposed) { return; } this.posx = posx; this.posy = posy; var foreCanvs = document.getElementById("_fore"); var newUnit = document.getElementById(this.id); if (newUnit == null) { newUnit = document.createElement("span"); newUnit.id = this.id; foreCanvs.appendChild(newUnit); } if (this.dead) { newUnit.style.display = ‘none‘; } newUnit.style.position = "absolute"; newUnit.style.left = this.posx; newUnit.style.top = this.posy; newUnit.style.width = this.width * this.unitWidth; newUnit.style.height = this.height * this.unitHeight; if (this.repeatable == "norepeat") { newUnit.style.backgroundRepeat = "no-repeat"; } newUnit.style.backgroundImage = "url(" + this.imageUrl + ")"; } GameUnit.prototype.Dispose = function () { this.Dispose = function () { }; //该函数只能被执行一次 this.AIMode = null; this.Move = function () { }; this.clearInterval(); var foreCanvs = $("_fore"); var unit = $(this.id); foreCanvs.removeChild(unit); //GameUnit.metaData[this.id] = null; for (var i = 0; i < GameUnit.metaData.All.length; i++) { if (GameUnit.metaData.All[i].id == this.id) { GameUnit.metaData.All.splice(i, 1); } } this.Disposed = true; } GameUnit.prototype.Region = function () //返回当前所在的区域 { for (var i = 0; i < Region.metaData.All.length; i++) { if (this == Region.metaData.All[i]) { continue; } if (this.Intersects(Region.metaData.All[i])) { return Region.metaData.All[i]; } } return null; } GameUnit.prototype.Unit = function () //返回当前遇到的物件 { for (var i = 0; i < GameUnit.metaData.All.length; i++) { if (this == GameUnit.metaData.All[i]) { continue; } if (this.Intersects(GameUnit.metaData.All[i])) { return GameUnit.metaData.All[i]; } } return null; } GameUnit.prototype.Overlaped = function () //判断是否和其他物件重叠 { if (this.posx == null || this.posy == null) { return false; } for (var i = 0; i < GameUnit.metaData.All.length; i++) { if (this == GameUnit.metaData.All[i] || GameUnit.metaData.All[i].posx == null || GameUnit.metaData.All[i].posy == null) { continue; } if (this.Intersects(GameUnit.metaData.All[i])) { return true; } } return false; } this._parseType(); } GameUnit.prototype = new BaseUnit(); GameUnit.mapImages = new Array(); GameUnit.mapImages["TankHuman"] = Resource.ROOT + Resource.IMG_TANKS_NORTH; GameUnit.mapImages["TankComputer"] = Resource.ROOT + Resource.IMG_TANKE_SOUTH; GameUnit.mapImages["BulletHuman"] = Resource.ROOT + Resource.IMG_BULLET; GameUnit.mapImages["BulletComputer"] = Resource.ROOT + Resource.IMG_BULLET; GameUnit.mapImages["Home"] = Resource.ROOT + Resource.IMG_HOME; GameUnit.mapImages["Block"] = Resource.ROOT + Resource.IMG_SBLOCK; GameUnit.metaData = new Array(); GameUnit.metaData.All = new Array(); |
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>坦克大战</title> <style type="text/css"> body { background-color:#000; margin: 0 0 0 0; width:100%;height:100%;} div#main {padding: 0 0 0 0;width:800px;height:500px;background:url(images/ground.bmp);} </style> </head> <script type="text/javascript" src="js/Common.js"></script> <script type="text/javascript" src="js/Config.js"></script> <script type="text/javascript" src="js/Base.js"></script> <script type="text/javascript" src="js/Region.js"></script> <script type="text/javascript" src="js/GameUnit.js"></script> <script type="text/javascript" src="js/KeyCodeList.js"></script> <script type="text/javascript" src="js/AIAction.js"></script> <script type="text/javascript"> var emTankIdx = 3; var emTanks = new Array(20); function enemyDestroyed() {//地方坦克死后重新升成一辆坦克 if (emTankIdx < emTanks.length) { var x, y; do { y = 10; x = Math.floor(Math.random() * 3) * 220 + 40; emTanks[emTankIdx].Test(x, y); } while (emTanks[emTankIdx].Overlaped()); emTanks[emTankIdx].Draw(x, y); emTanks[emTankIdx].ready(1); emTankIdx++; } } var killed = 0; </script> <script type="text/javascript"> var Game = function () { this.initialize.apply(this, arguments) }; Game.prototype = { initialize: function () { //水 var water1 = new Region("water1", 50, 2, "water"); water1.Draw(0, 150); //水 var water2 = new Region("water2", 50, 2, "water"); water2.Draw(0, 300); var iron = new Region("iron", 20, 2, "iron"); iron.Draw(250, 225); var myTank = new GameUnit("mytank", 1, 1, "TankHuman");//己方坦克 myTank.Draw(200, 460); myTank.whenDead = function () { alert("你死了,你总共歼灭了" + killed + "辆坦克"); location.href = location.href; } var home = new GameUnit("home", 1, 1, "Home");//老家 home.Draw(400, 460); home.whenDead = function () { alert("Game Over!你总共歼灭了" + killed + "辆坦克"); location.href = location.href; } //保护老家的砖头块 var block1 = new GameUnit("block1", 1, 1, "Block"); var block2 = new GameUnit("block2", 1, 1, "Block"); var block3 = new GameUnit("block3", 1, 1, "Block"); var block4 = new GameUnit("block4", 1, 1, "Block"); var block5 = new GameUnit("block5", 1, 1, "Block"); var block6 = new GameUnit("block6", 1, 1, "Block"); var block7 = new GameUnit("block7", 1, 1, "Block"); var block8 = new GameUnit("block8", 1, 1, "Block"); var block9 = new GameUnit("block9", 1, 1, "Block"); block1.Draw(380, 480); block2.Draw(380, 464); block3.Draw(380, 448); block4.Draw(396, 448); block5.Draw(412, 448); block6.Draw(428, 448); block7.Draw(444, 448); block8.Draw(444, 464); block9.Draw(444, 480); //敌方坦克 for (var i = 0; i < emTanks.length; i++) { var id = "tank" + i; emTanks[i] = new GameUnit(id, 1, 1, "TankComputer"); emTanks[i].whenDead = function () {//一辆坦克死后过两秒重新生成一辆坦克 killed++; setTimeout("enemyDestroyed()", 2000); }; } emTanks[0].Draw(40, 10); emTanks[0].ready(1); emTanks[1].Draw(480, 10); emTanks[1].ready(1); emTanks[2].Draw(260, 10); emTanks[2].ready(1); } } window.onload = function () { new Game(); } </script> <body> <div id="main"> <div id="_back"> </div> <div id="_fore"> </div> </div> </body> </html> |
原文:http://blog.csdn.net/starcuan/article/details/19214895