《游戏脚本的设计与开发》-(战棋部分)2.4 物理攻击

终于到了攻击部分了,战棋游戏中的攻击,主要分为物理攻击和法术攻击,本章就先从物理攻击讲起。物理攻击又分为普通攻击,连击(双击),以及致命攻击,再复杂一点的还有其他特殊攻击,比如我的《三国记-乱世群雄》游戏里面,张飞的三次攻击,关羽的多人攻击等特殊的技能攻击。我依然从简单开始,先来看看如何来实现一下普通攻击,攻击的过程是,1,A对B进行攻击。2,B受伤或者档格。3,如果A在B的攻击范围之内,则B会进行一次反击。4,A受伤或者档格。

上一章发布之后,隔的时间比较长了,主要是工作忙,没太多时间整理这些东西,下面我主要介绍一下代码的核心部分,最后有源码下载,大家可以对照着文章看一下,方便理解。

攻击菜单

上一章已经解决了人物移动,接下来当人物移动结束的时候,需要弹出一个攻击选择,先看看攻击菜单的制作。

ffunction LSouSouSMapMenuCtrl(x,y){
	var self = this;
	base(self,LSprite,[]);
	self.addMenu(x,y);
}
LSouSouSMapMenuCtrl.prototype.addMenu = function(x,y){
	var self = this;
	LSouSouObject.sMap.smapClick.removeClickEvent(); 
	var menuLayer = new LSprite();
	var buttonAtk = new LButtonSample1("攻击");
	buttonAtk.backgroundCorl = "red";
	buttonAtk.x = 15;
	buttonAtk.y = 15;
	menuLayer.addChild(buttonAtk);
	var buttonTactics = new LButtonSample1("策略");
	buttonTactics.x = 15;
	buttonTactics.y = buttonAtk.getHeight() + buttonAtk.y;
	menuLayer.addChild(buttonTactics);
	var buttonProps = new LButtonSample1("道具");
	buttonProps.x = 15;
	buttonProps.y = buttonAtk.getHeight()*2 + buttonAtk.y;
	menuLayer.addChild(buttonProps);
	var buttonStop = new LButtonSample1("停止");
	buttonStop.x = 15;
	buttonStop.y = buttonAtk.getHeight()*3 + buttonAtk.y;
	menuLayer.addChild(buttonStop);
	var buttonCancel = new LButtonSample1("取消");
	buttonCancel.backgroundCorl = "red";
	buttonCancel.x = 15;
	buttonCancel.y = buttonAtk.getHeight()*4 + buttonAtk.y;
	menuLayer.addChild(buttonCancel);
	var selfWidth = buttonAtk.getWidth()+30;
	var selfHeight = buttonAtk.getHeight()*5+30;
        var bar = LSouSouObject.getBar(selfWidth,selfHeight);
	menuLayer.addChild(bar);
	self.addChild(menuLayer);
	x += LSouSouObject.sMap.nodeLength;
	if(x + LSouSouObject.sMap.backLayer.x + selfWidth > LSouSouObject.sMap.SCREEN_WIDTH){
		x -= (selfWidth + LSouSouObject.sMap.nodeLength); 
	}
	if(y + LSouSouObject.sMap.backLayer.y + selfHeight > LSouSouObject.sMap.SCREEN_HEIGHT){
		y = LSouSouObject.sMap.SCREEN_HEIGHT - selfHeight - LSouSouObject.sMap.backLayer.y;
	}
	self.x = x + LSouSouObject.sMap.backLayer.x;
	self.y = y + LSouSouObject.sMap.backLayer.y;
	
	buttonAtk.addEventListener(LMouseEvent.MOUSE_UP,self.onclickAtk);
	buttonCancel.addEventListener(LMouseEvent.MOUSE_UP,self.onclickCancel);
};
LSouSouSMapMenuCtrl.prototype.onclickAtk = function(e){
};
LSouSouSMapMenuCtrl.prototype.onclickCancel = function(e){
};

下面的一行代码是上一章中移动结束事件的添加

LSouSouObject.charaSNow.addEventListener(LSouSouEvent.CHARACTER_MOVE_COMPLETE,self.onShowAttackMenu);

修改如下

LSouSouObject.charaSNow.addEventListener(LSouSouEvent.CHARACTER_MOVE_COMPLETE,self.onMoveComplete);
相应的回调函数onMoveComplete如下。
LSouSouSMap.prototype.onMoveComplete = function(){
	var self = LSouSouObject.sMap;
	LSouSouObject.charaSNow.removeEventListener(LSouSouEvent.CHARACTER_MOVE_COMPLETE,self.onMoveComplete);
	//移动结束
	if(LSouSouObject.charaSNow.belong == LSouSouObject.BELONG_SELF){
		self.showCtrlMenu();
	}else{
		//敌军AI,暂略
	}
};

上面代码,当移动结束的人物所属是我军的时候,调用showCtrlMenu函数,如下

LSouSouSMap.prototype.showCtrlMenu = function(){
	var self = this;
	self.menu = LSouSouSMapMenu.addSMenu(LSouSouObject.charaSNow.x,LSouSouObject.charaSNow.y,"ctrl");
	self.menuLayer.addChild(self.menu);
};

这样就添加了攻击菜单了,效果如下


攻击范围

点击上面的选择列表中的攻击按钮,首先应该确定攻击范围。

在arms.json兵种设定文件中,攻击范围主要是由两个属性决定,RangeAttack是攻击的范围,RangeAttackTarget是攻击时一次攻击能攻击到的人数范围,比如下面的游戏截图中,红色区域由RangeAttack决定,绿色区域由RangeAttackTarget决定。


首先来显示攻击范围,修改LSouSouSMapMenuCtrl类的onclickAtk函数,如下。

LSouSouSMapMenuCtrl.prototype.onclickAtk = function(e){
	var i,nodeChild;

	LSouSouObject.sMap.menu.parent.removeChild(LSouSouObject.sMap.menu);
	LSouSouObject.sMap.menu = null;
	var attackRange = LSouSouObject.sMap.attackRange = LSouSouObject.charaSNow.member.getRangeAttack();
	for(i=0;i<attackRange.length;i++){
		nodeChild = attackRange[i];
		LSouSouObject.sMap.attackRangeLayer.graphics.drawRect(1,"#000000",
			[nodeChild.x*LSouSouObject.sMap.nodeLength + LSouSouObject.charaSNow.x - LSouSouObject.sMap.backLayer.x,
			nodeChild.y*LSouSouObject.sMap.nodeLength + LSouSouObject.charaSNow.y - LSouSouObject.sMap.backLayer.y,
			LSouSouObject.sMap.nodeLength,LSouSouObject.sMap.nodeLength],true,"#FF0000");
	}
	LSouSouObject.sMap.menu = LSouSouSMapMenu.addSMenu(LSouSouObject.charaSNow.x,LSouSouObject.charaSNow.y,"cancel");
	LSouSouObject.sMap.menuLayer.addChild(LSouSouObject.sMap.menu);
	LSouSouObject.sMap.menu.clickEvent = function(event){
		LSouSouObject.sMap.menu.parent.removeChild(LSouSouObject.sMap.menu);
		LSouSouObject.sMap.menu = null;
		LSouSouObject.sMap.attackRangeLayer.graphics.clear();
		LSouSouObject.sMap.showCtrlMenu();
	};
	LSouSouObject.sMap.smapClick.setAtkClickEvent();
};

其中的一行

LSouSouSMapMenu.addSMenu(LSouSouObject.charaSNow.x,LSouSouObject.charaSNow.y,"cancel");

是添加一个取消按钮,用处是当点击攻击按钮之后,可以取消之后重新选择,在flash版三国记中取消按钮是被我添加到了右侧的按钮列表,如下


这样的话,用户体验上不太好,所以这次我把取消按钮添加到攻击者的位置上,取消按钮代码如下。

function LSouSouSMapMenuCancel(x,y){
	var self = this;
	base(self,LSprite,[]);
	self.clickEvent = null;
	self.addMenu(x,y);
}
LSouSouSMapMenuCancel.prototype.addMenu = function(x,y){
	var self = this;
	var menuLayer = new LSprite();
	var buttonCancel = new LButtonSample1("×");
	menuLayer.addChild(buttonCancel);
	self.addChild(menuLayer);
	self.x = x + LSouSouObject.sMap.backLayer.x + (LSouSouObject.sMap.nodeLength - buttonCancel.getWidth())*0.5;
	self.y = y + LSouSouObject.sMap.backLayer.y + (LSouSouObject.sMap.nodeLength - buttonCancel.getHeight())*0.5;
	buttonCancel.addEventListener(LMouseEvent.MOUSE_UP,self.onclickCancel);
};
LSouSouSMapMenuCancel.prototype.onclickCancel = function(e){
	var self = LSouSouObject.sMap.menu;
	if(self.clickEvent)self.clickEvent();
};

运行程序后,效果如下。



攻击

最后,就是当点击攻击的范围,并且点击的位置有敌军存在的话,就开始攻击,点击事件如下

LSouSouSMapClick.prototype.onAtkClickUp = function(e){
	trace("onAtkClickUp is click");
	var mx = e.selfX;
	var my = e.selfY;
	var intX = ((mx - LSouSouObject.sMap.backLayer.x)/LSouSouObject.sMap.nodeLength) >>> 0;
	var intY = ((my - LSouSouObject.sMap.backLayer.y)/LSouSouObject.sMap.nodeLength) >>> 0;
	var nodeStr;
	var nodeArr;
	var _characterS;
	var list = LSouSouObject.sMap.enemylist;
	for(i=0;i
  
  
   
    _characterS.x + LSouSouObject.sMap.backLayer.x && 
			mx < _characterS.x + LSouSouObject.sMap.backLayer.x + LSouSouObject.sMap.nodeLength && 
			my > _characterS.y + LSouSouObject.sMap.backLayer.y && 
			my < _characterS.y + LSouSouObject.sMap.backLayer.y + LSouSouObject.sMap.nodeLength){
			LSouSouObject.sMap.menu.parent.removeChild(LSouSouObject.sMap.menu);
			LSouSouObject.sMap.menu = null;
			LSouSouObject.sMap.attackRangeLayer.graphics.clear();
			LSouSouObject.charaSNow.targetCharacter = _characterS;
	trace("LSouSouObject.charaSNow.targetCharacter="+LSouSouObject.charaSNow.targetCharacter);
			//攻击
			LSouSouObject.charaSNow.setAttackNumber();
			LSouSouObject.charaSNow.attackCalculate();
		}
	}
};
  
  

上面的代码,当点击到敌军的时候,首先调用LSouSouCharacterS类的setAttackNumber函数,然后调用attackCalculate函数,setAttackNumber函数是决定攻击次数,比如双击等多次攻击的判定是在这个函数中决定的,本次不考虑多次攻击,所以setAttackNumber函数中的攻击次数是默认为1的,如下

LSouSouCharacterS.prototype.setAttackNumber = function(){
	var self = this;
	//双击判定 暂略

	self.attackNumber = 1;
	self.attackIndex = 0;
	self.targetCharacter.attackIndex = 0;
	self.targetCharacter.attackNumber = 1;
};

attackCalculate函数,包含攻击之前的特技等判断,暂时也不考虑,如下

LSouSouCharacterS.prototype.attackCalculate = function(){
	var self = this;
	
	//特技判定 暂略

	//物理攻击
	self.toAttackStart();
};

然后调用toAttackStart函数,开始物理攻击,代码如下。

LSouSouCharacterS.prototype.toAttackStart = function(){
	var self = this;
	trace("self.targetCharacter="+self.targetCharacter);
	if(!self.targetCharacter)return;
	if(self.x > self.targetCharacter.x){
		self.action = LStaticSouSouCharacterS.ATK_LEFT;
	}else if(self.x < self.targetCharacter.x){
		self.action = LStaticSouSouCharacterS.ATK_RIGHT;
	}else if(self.y > self.targetCharacter.y){
		self.action = LStaticSouSouCharacterS.ATK_UP;
	}else{
		self.action = LStaticSouSouCharacterS.ATK_DOWN;
	}
	self.action_mode = LStaticSouSouCharacterS.MODE_ATTACK;
	self.addEventListener(LSouSouEvent.ANIMATION_COMPLETE,self.attackOver);
};

attackOver函数是当攻击结束时调用的,代码如下。

LSouSouCharacterS.prototype.attackOver = function(){
	var self = this;
	var charas,i;
	var target = self.targetCharacter;
	var checkBelong = LSouSouObject.sMap.belong_mode;
	self.removeEventListener(LSouSouEvent.ANIMATION_COMPLETE,self.attackOver);
	self.attackIndex++;
	//双击或双击以上攻击 暂略

	//对方没有混乱,兵力大于0,并且可以反击
	if(self.targetCharacter.member.getTroops() > 0
		 && self.targetCharacter.attackIndex < self.targetCharacter.attackNumber){
		self.action = LStaticSouSouCharacterS.DOWN + self.direction;
		for(i=0;i<self.targetArray.length;i++){
			charas = self.targetArray[i];
			if(charas.action_mode == LStaticSouSouCharacterS.MODE_STOP){
				charas.action = LStaticSouSouCharacterS.DOWN + charas.direction;
			}else{
				charas.action = LStaticSouSouCharacterS.MOVE_DOWN + charas.direction;
			}
		}
		trace("self.targetCharacter.targetCharacter="+self.targetCharacter.targetCharacter);
		trace("LSouSouCharacterSAI="+LSouSouCharacterSAI.atAttackRect(self.targetCharacter,self.locationX(),self.locationY()));
		if(self.targetCharacter.targetCharacter && 
			LSouSouCharacterSAI.atAttackRect(self.targetCharacter,self.locationX(),self.locationY())){
			self.targetCharacter.attackCalculate();
		}else{
			self.action_mode = LStaticSouSouCharacterS.MODE_STOP;
			self.targetCharacter.targetCharacter = null;
			self.targetCharacter = null;
		}
	}else{
		for(i=0;i<self.targetArray.length;i++){
			charas = self.targetArray[i];
			if(charas.action_mode == LStaticSouSouCharacterS.MODE_STOP){
				charas.action = LStaticSouSouCharacterS.DOWN + charas.direction;
			}else{
				charas.action = LStaticSouSouCharacterS.MOVE_DOWN + charas.direction;
			}
		}
		if(self.belong == LSouSouObject.charaSNow.belong){
			self.action_mode = LStaticSouSouCharacterS.MODE_STOP;
			self.action = LStaticSouSouCharacterS.DOWN + this.direction;
			self.targetCharacter.targetCharacter = null;
			self.targetCharacter = null;
		}else{
			self.action = LStaticSouSouCharacterS.MOVE_DOWN + this.direction;
			self.targetCharacter.action = LStaticSouSouCharacterS.DOWN + self.targetCharacter.direction;
			self.targetCharacter.action_mode = LStaticSouSouCharacterS.MODE_STOP;
			self.targetCharacter.targetCharacter = null;
			self.targetCharacter = null;
		}
	}
};

上面代码中,有攻击者还是反击者等判断,toAttackStart和attackOver函数,只是攻击开始和攻击结束的处理,至于攻击过程,掉血还是档格等,是在攻击动作的第三个动作时开始处理的,LSouSouCharacterS的贞函数中有下面的代码。

	if(self.action_mode == LStaticSouSouCharacterS.MODE_ATTACK){
		self.toAttackTargets();
	}

就是说当人物处在攻击动作时,调用toAttackTargets函数,相关代码如下。

LSouSouCharacterS.prototype.toAttackTargets = function(){
	var self = this,charas,_charalist,i;
	if(!self.targetCharacter)return;
	if(self.actionIndex == 2){
		_charalist = {};
		if(self.belong == LSouSouObject.BELONG_SELF || self.belong == LSouSouObject.BELONG_FRIEND){
			for(i=0;i<LSouSouObject.sMap.enemylist.length;i++){
				charas = LSouSouObject.sMap.enemylist[i];
				if(!charas.visible)continue;
				_charalist[charas.locationX() + "," + charas.locationY()] = charas;
			}
		}else{
			for(i=0;i<LSouSouObject.sMap.ourlist.length;i++){
				charas = LSouSouObject.sMap.ourlist[i];
				if(!charas.visible)continue;
				_charalist[charas.locationX() + "," + charas.locationY()] = charas;
			}
			for(i=0;i<LSouSouObject.sMap.friendlist.length;i++){
				charas = LSouSouObject.sMap.friendlist[i];
				if(!charas.visible)continue;
				_charalist[charas.locationX() + "," + charas.locationY()] = charas;
			}
		}
		self.targetArray = [];
		var node,nodeArr,rangeAttackTarget = self.member.getRangeAttackTarget();
		for(i=0;i
  
  
   
   = charas.member.getTroops()){
			hertValue = charas.member.getTroops();
			//获取经验值计算 暂略
		}
		//特技修正 暂略
		//减HP 暂略
		charas.action = LStaticSouSouCharacterS.HERT;
		
		//减HP显示 
		var numberShow = new LSouSouSMapNumber(-hertValue);
		numberShow.x = charas.x + (charas.getWidth() - numberShow.getWidth())*0.5;
		numberShow.y = charas.y + (charas.getHeight() - numberShow.getHeight())*0.5;
		LSouSouObject.sMap.charaLayer.addChild(numberShow);
	}else{
		//档格
		if(charas.member.getIndex() == self.targetCharacter.member.getIndex()){
			charas.targetCharacter = self;
		}
		if(self.x > charas.x){
			charas.action = LStaticSouSouCharacterS.BLOCK_RIGHT;
		}else if(self.x < charas.x){
			charas.action = LStaticSouSouCharacterS.BLOCK_LEFT;
		}else if(self.y > charas.y){
			charas.action = LStaticSouSouCharacterS.BLOCK_DOWN;
		}else{
			charas.action = LStaticSouSouCharacterS.BLOCK_UP;
		}
	
	}
	//升级判定 暂略
}
  
  

下面是攻击效果

测试连接如下

http://lufylegend.com/demo/test/lsharp/11/game/index.html

以上,本章就先讲这么多了,下一章会讲一下我方,敌方和友方回合之间的切换,以及敌军和友军方面简单的攻击AI

本章为止的源码如下,不包含lufylegend.js引擎源码,请自己到官网下载

http://lufylegend.com/demo/test/lsharp/11/11.rar

※源码运行说明:需要服务器支持,详细请看本系列文章《序》和《第一章》

《游戏脚本的设计与开发》系列文章目录

http://blog.csdn.net/lufy_legend/article/details/8888787

本章就讲到这里,欢迎继续关注我的博客

转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend
  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
### 回答1: Unity战棋游戏源码是一个用Unity引擎制作的战棋游戏的程序代码。战棋游戏是一种策略类游戏,玩家可以控制虚拟角色在棋盘上进行战斗和策略决策。 该源码可能包含以下功能: 1. 战斗系统:具备基本的攻击、防御和回合制战斗机制。玩家可以选择不同的角色,每个角色都有独特的技能和属性。在回合制中,玩家可以选择移动、攻击或使用技能来消灭敌人。 2. 地图编辑器:玩家可以使用地图编辑器创建自己的游戏地图。地图编辑器允许玩家在创建地形、放置建筑物和障碍物等方面进行自定义。 3. 单人模式和多人模式:玩家可以选择单人模式与电脑对战,也可以选择多人模式与其他玩家进行实时对战。 4. AI系统:游戏可能会包含一个AI系统,使电脑角色能够自动进行决策和行动。AI系统可能根据玩家的行为和游戏规则来进行智能决策。 5. 游戏进度和存档:游戏可能有一个进度和存档系统,玩家可以保存和加载游戏进度,以便在需要时继续游戏。 总的来说,Unity战棋游戏源码提供了一个基础的战棋游戏框架,玩家可以通过自定义和扩展代码来创建自己的战棋游戏。这个源码可以帮助开发者学习和理解战棋游戏设计和实现,为他们创作属于自己的战棋游戏提供了有力的支持。 ### 回答2: Unity战棋游戏源码是一种可以用于构建战棋游戏的程序代码。Unity是一款非常流行的游戏引擎,它提供了强大的开发工具和功能,可以帮助开发者快速构建高质量的游戏战棋游戏是一种策略类型的游戏,玩家根据自己的决策和策略来战胜敌对玩家或者电脑AI。 在Unity战棋游戏源码中,通常包含了游戏的主要逻辑,如游戏角色的移动、攻击、技能、状态转换等。此外,还会涵盖游戏场景的构建、动画和特效的制作、战斗系统的设计等方面。 使用Unity战棋游戏源码,开发者可以节省大量的开发时间和精力。源码提供了一个已经搭建好的框架和基础代码,使得开发者可以专注于游戏的核心玩法和美术资源的制作。同时,开发者可以根据自己的需求对源码进行修改和定制,以便创建出自己独特的战棋游戏。 总之,Unity战棋游戏源码是一种开发工具,可以帮助开发者快速构建战棋游戏。它提供了一个已经搭建好的框架和基础代码,使开发者可以专注于游戏的核心玩法和美术资源的制作,为玩家带来高质量的游戏体验。
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值