[原创] 使用代码控制人物动作

原创首发[url]www.eb163.com[/url],转载请保留出处 作者:iiacm(着火骷髅)
  
  游戏里,人物的换装、走动、打斗等等行为都会用动画或者图片来表现、构造,如果使用3D进行建模,自然能水到渠成。但最终还是要输出成图像,如果人物的动作设定很多,那么就有可能需要制作大量的动画来支撑。那有没有可能不通过设计大量的图片或动画就能使人物有丰富的动作呢?本帖子仅是我的个人思考和试验,或许并不能从根本解决问题,不过试验本身都是有趣的,所以我们不妨来看看这样一种方案:使用脚本解析人物动作,包括挥舞肢体和更换外观(样貌与五官,乃至服饰)。这个例子只适用于2D的游戏人物,并且如果需要更强大的动作,则对各组件也必须设计得更加精巧,基本上应该算是个2D的人物骨骼了。顺带提一提,这个试验是我在网上看到一个使用as 2.0脚本控制火柴骨骼挥动的例子时,使我想起曾经也做过类似的东西,其中有共通的地方,于是就拿出来和大家一起探讨探讨,脚本控制火柴骨骼帖子 地址:[url]http://www.eb163.com/club/thread-11196-1-1.html[/url]
  效果展示地址:[url]http://www.eb163.com/club/thread-11197-1-1.html[/url]
(由于动作间隔太短,可以刷新浏览器后观察,点击人物可以时木偶部件更换颜色)
  首先,需要有个可以控制的角色,这个角色有点像是个可以重复使用的木偶人,让我想起了素还真……
  为了简便,只制作了一个比较Q的东西,二头身,缩短开发时间,当然,如果愿意,我想可以制作一个更加精密的“木偶”,精密程度关系到木偶可以做出的行为复杂度,连带的,他们的服装和五官、发型等都将要愈加的精致。和直接设计所有人物行为动作不同的是,这种木偶式的设计,可以筹划、设计一次,使用多次。不过问题还是有的,就是它不可能适应所有的情况,只能是种在适合的情况下的一种可选方案。
  木偶的制作过程需要比较细致的规划,要点是肢体的划分,为了能更细致的控制躯体,即便是二头身的人偶,我也精确到了膝盖和小臂关节,好使他们能“运动自如”(仅仅是比较的自如)。

木偶关节
[img]http://dl.iteye.com/upload/attachment/288965/aa5bda7f-1431-3347-ab82-1713a792a446.jpg[/img]

  在关节处,也就是肢体围绕作旋转运动的地方定位,在代码中保留引用,可以在初始化身体的时候进行位置校对。所有肢体元件和后面提到的木偶部件都会被分类存放,并且设置链接标识,和Sprite类作好关联,在代码中会被用到。

木偶全身
[img]http://dl.iteye.com/upload/attachment/288967/0536b937-91c5-35cf-a1c1-37d705696992.jpg[/img]

木偶部件分类
[img]http://dl.iteye.com/upload/attachment/288969/1bc9603c-4333-3124-920a-b72d8ec468c3.jpg[/img]

  大致符合想要的人物比例即可。如果有需要可以多做几种体格的人偶来满足开发需求。接下来要做好脸蛋的轮廓,因为是二头身,所以这个脸蛋就…… 这里值得说明的是,我只做了男性和女性的脸蛋轮廓,实际上可以多做几种,比如老人、奸贼、忠诚、反贼…… - -# 无论脸蛋轮廓有多少种,其颈脖位置也要做准确的定位,并在代码中给出引用,并且要熟悉不同元件坐标的转换。这个工作是木偶的关键。


[img]http://dl.iteye.com/upload/attachment/288971/74acb265-a883-3b1e-8d0d-8118689efa00.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/288973/3cb163c6-3cf4-39b9-9133-b9f500a66d61.jpg[/img]

  然后就可以开始设计木偶身上的部件了:发型、眼睛、鼻子、嘴巴、眉毛、修饰(伤疤、各种皱纹、刺青、独眼龙眼罩等)。

[img]http://dl.iteye.com/upload/attachment/288975/9534b824-0598-3f53-af81-cab9ffa7f80a.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/288977/6098f9c9-8e71-3ad6-ae59-9483e7fdbf84.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/288979/4fdacfec-2bc1-3910-b5d9-1bd108f3c7e8.jpg[/img]

  接着就到代码部分了。由于代码比较多,这里只贴出部分主要的代码片段,对整个控制过程进行基本的描述(不过做的过程中未对代码进行全程注释- -!):

import kw.game.charactorBuilder.Knight;
import kw.game.charactorBuilder.CharactorData;
import kw.game.charactorBuilder.head.FaceData;
import kw.game.charactorDataParser.BodyRotation;
import flash.geom.ColorTransform;
import flash.geom.Transform;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.utils.getTimer;
//新增 6 个人物
var aC1:Knight=new Knight();
var aC2:Knight=new Knight();
var aC3:Knight=new Knight();
var aC4:Knight=new Knight();
var aC5:Knight=new Knight();
var aC6:Knight=new Knight();
var cArr:Array=new Array();
/*设置各个人物的外貌,FaceData类负责存储样貌信息,传递给 Knight 中的 knight 属性的setFace() 方法进行设置样貌,其顺序非别标识:性别、发型、眉毛、眼睛、鼻子*/
aC1.knight.setFace(new FaceData(0,1,4,1,1));
aC2.knight.setFace(new FaceData(1,2,1,2,0));
aC3.knight.setFace(new FaceData(0,3,2,4,1));
aC4.knight.setFace(new FaceData(1,1,2,1,0));
aC5.knight.setFace(new FaceData(0,4,3,3,2));
aC6.knight.setFace(new FaceData(0,6,4,0,2));
//放入数组
cArr.push(aC1,aC2,aC3,aC4,aC5,aC6);
//对所有人物进行操作
for (var i:int=0; i<cArr.length; i++) {
cArr[i].name="f"+i;
     /*这里调用人物的肢体控制函数 knightPosture 我称它为 “摆pose函数”,传入的字符串包含了我制定的规则,使用"|" 符号区分动作序列,有点类似动画帧,":"符号区分同肢体上不同作用的参数,","号为发挥同一作用的一组参数集,这组字符串会让 kw.game.charactorDataParser.CharactorDataParser 类处理,这个类专门解析这组字符串,并生成一个动作序列,然后发给 Charactor 类,也就是人物角色类,让人物角色类执行动作序列,以达到人物动起来的目的。*/
cArr[i].knightPosture("100:1,0,0:5,0,15,120,25,90,35,0,0,0,0:0:0:0|100:1,0,0:5,0,0,20,25,45,-35,5,0,5,0:6:0:0|100:1,0,0:5,0,0,15,12,45,60,0,15,0,10:6:0:0|100:1,0,0:5,0,15,120,25,90,35,0,0,0,0:0:0:0|100:1,0,0:5,0,0,20,25,45,-35,5,0,5,0:6:0:0|100:1,0,0:5,0,0,15,12,45,60,0,15,0,10:6:0:0");
addChild(cArr[i]);
cArr[i].x=70*i;
cArr[i].y=120;
cArr[i].addEventListener(MouseEvent.MOUSE_UP,onFUp);
function onFUp(e) {
var red:uint=0;
var blue:uint=0;
var green:uint=0;
red=225*Math.random();
green=225*Math.random();
blue=225*Math.random();
var colorTr:ColorTransform=new ColorTransform(1,1,1,1,red,green,blue,0);
e.target.transform.colorTransform=colorTr;
trace(e.target.name);
}
}



package kw.game.charactorDataParser
{
import flash.geom.Point;
import kw.game.charactorBuilder.CharactorData;

/**
* ...
* @author ...
*/
public class CharactorDataParser
{
public static var ACTIVE_GROUP_FLAG:String = "|";
public static var CHARACTOR_GROUP_FLAG:String = ":";
public static var BASE_FLAG:String = ",";
public function CharactorDataParser() {

}
//时间:水平距离,垂直距离,高度:头部角,身体角,腰部角,右手上臂角,右手下臂角,左手上臂角,左手下臂角,右脚大腿角,右脚小腿角,左脚大腿角,左脚小腿角:短武器角:特效编号:特效发生部位|...
public function parseData(dataStr:String):Array {
var retArr:Array=new Array();
var activeArr:Array = dataStr.split(ACTIVE_GROUP_FLAG);
for (var i:int = 0; i < activeArr.length;i++ ) {
var bodyParseData:BodyParseData=new BodyParseData();
var str:String = activeArr[i];
var groupArr:Array = str.split(CHARACTOR_GROUP_FLAG);
var moveArr:Array = new Array();
var rotationArr:Array = new Array();
var rotationData:BodyRotation=new BodyRotation();
bodyParseData.time = groupArr[0];
moveArr = groupArr[1].split(BASE_FLAG);
bodyParseData.movePoint = new Point(moveArr[0], moveArr[1]);
bodyParseData.hight = moveArr[2];
rotationArr = groupArr[2].split(BASE_FLAG);
for (var j:int = 0; j < rotationArr.length;j++ ) {
rotationData[BodyRotation.propertyList[j]]=rotationArr[j];
}
bodyParseData.bodyRotation = rotationData;
bodyParseData.weaponRotation = groupArr[3];
bodyParseData.effectsIndex = groupArr[4];
bodyParseData.effectsBodyPos = groupArr[5];
retArr.push(bodyParseData);
}
return retArr;
}
}

}



package kw.game.charactorBuilder{
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.geom.Point;
import flash.utils.Timer;
import kw.game.charactorBuilder.head.FaceData;
import kw.game.charactorDataParser.BodyParseData;
import kw.game.charactorBuilder.body.*;
import kw.game.charactorBuilder.CharactorData;
import kw.game.charactorBuilder.handLeg.HandLeg;
import kw.game.charactorBuilder.head.Head;
import kw.game.charactorDataParser.BodyRotation;
/**
* ...
* @author ...
*/
public class Charactor extends Sprite {
//-------相貌剪辑名称数组
protected var faceMcNameArr:Array = ["face_mc", "blow_mc", "eye_mc", "nose_mc", "hair_mc"];
//-------相貌类构造函数索引
protected var faceList:Array = ["blowArr", "eyeArr", "noseArr", "hairArr"];
//-------FaceData类属性清单
protected var faceDataList:Array = ["blow", "eye", "nose", "hair"];
//-------相貌类构造函数各性别清单
protected var faceArr:Array = [CharactorData.MAIL_FACE_C, CharactorData.FEMAIL_FACE_C];
           //-------
protected var MhairArr:Array = [CharactorData.MHAIR_0, CharactorData.MHAIR_1, CharactorData.MHAIR_2, CharactorData.MHAIR_3, CharactorData.MHAIR_4, CharactorData.MHAIR_5, CharactorData.MHAIR_6, CharactorData.MHAIR_7, CharactorData.MHAIR_8, CharactorData.MHAIR_9, CharactorData.MHAIR_10, CharactorData.MHAIR_11, CharactorData.MHAIR_12];
protected var FhairArr:Array = [CharactorData.FHAIR_0, CharactorData.FHAIR_1, CharactorData.FHAIR_2, CharactorData.FHAIR_3, CharactorData.FHAIR_4, CharactorData.FHAIR_5];
protected var MblowArr:Array = [CharactorData.MBLOW_0, CharactorData.MBLOW_1, CharactorData.MBLOW_2, CharactorData.MBLOW_3, CharactorData.MBLOW_4, CharactorData.MBLOW_5, CharactorData.MFBLOW_0];
protected var FblowArr:Array = [CharactorData.FBLOW_0, CharactorData.FBLOW_1, CharactorData.FBLOW_2];//, CharactorData.MBLOW_3, CharactorData.MBLOW_4, CharactorData.MBLOW_5
protected var MeyeArr:Array = [CharactorData.MEYE_0, CharactorData.MEYE_1, CharactorData.MEYE_2, CharactorData.MEYE_3, CharactorData.MEYE_4];
protected var FeyeArr:Array = [CharactorData.FEYE_0, CharactorData.FEYE_1, CharactorData.FEYE_2];
protected var MnoseArr:Array = [CharactorData.MNOSE_0, CharactorData.MNOSE_1, CharactorData.MNOSE_2];
protected var FnoseArr:Array = [CharactorData.FNOSE_0];
//protected var FblowArr:Array = [CharactorData.MBLOW_1];
//-------------
public var face_mc:Sprite;
public var hair_mc:Sprite;
//------------------
public static var COLOR_HAIR:Array;//[{red:157, green:26, blue:2},{}]
//--------------------
public static var HEAD:String="head_mc";
public static var RIGHT_HAND:String="rightHand_mc";
public static var RIGHT_HAND_2:String="rightHand2_mc";
public static var LEFT_HAND:String="leftHand_mc";
public static var LEFT_HAND_2:String="leftHand2_mc";
public static var BODY:String="body_mc";
public static var BODY_2:String="body2_mc";
public static var RIGHT_LEG:String="rightLeg_mc";
public static var RIGHT_LEG_2:String="rightLeg2_mc";
public static var LEFT_LEG:String="leftLeg_mc";
public static var LEFT_LEG_2:String = "leftLeg2_mc";
public static var WEAPON:String = "weapon_mc";
//---------------------------
protected var _head:Head;
protected var _rightHand:HandLeg;
protected var _rightHand2:HandLeg;
protected var _leftHand:HandLeg;
protected var _leftHand2:HandLeg;
protected var _body:BodyPart;
protected var _body2:BodyPart;
protected var _rightLeg:HandLeg;
protected var _rightLeg2:HandLeg;
protected var _leftLeg:HandLeg;
protected var _leftLeg2:HandLeg;
//----------
protected var bodyPartMcArr:Array;
//--------
protected var postureArr:Array;
protected var postureIndex:int = 0;
protected var postureTimer:Timer;
//-------------
protected var charactorFaceData:FaceData;
public function Charactor() {
init();
}
public function refreshBody(tuneData:BodyParseData){
this.x += tuneData.movePoint.x;
this.y += tuneData.movePoint.y;
_head.rotation=tuneData.bodyRotation.headR;
_body.rotation=tuneData.bodyRotation.bodyR;
_body2.rotation=tuneData.bodyRotation.body2R;
_rightHand.rotation=tuneData.bodyRotation.rightHandR;
_rightHand2.rotation=tuneData.bodyRotation.rightHand2R;
_leftHand.rotation=tuneData.bodyRotation.leftHandR;
_leftHand2.rotation=tuneData.bodyRotation.leftHand2R;
_rightLeg.rotation=tuneData.bodyRotation.rightLegR;
_rightLeg2.rotation=tuneData.bodyRotation.rightLeg2R;
_leftLeg.rotation=tuneData.bodyRotation.leftLegR;
_leftLeg2.rotation = tuneData.bodyRotation.leftLeg2R;
//--------
for(var i:int=1;i<bodyPartMcArr.length;i++){
bodyPartMcArr[i].refreshBodyPart();
}
}
public function tuneBody(arr:Array) {
var tuneData:BodyParseData;
if (postureIndex == 0) {
postureArr = arr;
tuneData = arr[postureIndex];
postureTimer = new Timer(tuneData.time, 1);
postureTimer.addEventListener(TimerEvent.TIMER, onPostureTime);
postureTimer.start();
}else{
tuneData = arr[postureIndex];
postureTimer = new Timer(tuneData.time, 1);
postureTimer.addEventListener(TimerEvent.TIMER, onPostureTime);
postureTimer.start();
}
function onPostureTime(e:TimerEvent) {
postureIndex++;
refreshBody(tuneData);
if (postureIndex < arr.length) {
postureTimer.removeEventListener(TimerEvent.TIMER, onPostureTime);
tuneBody(arr);
}else {
postureTimer.removeEventListener(TimerEvent.TIMER, onPostureTime);
}
}
}
public function setFace(faceData:FaceData) {//sex, hair, brow, eye, nose, mounth, furrow, other
charactorFaceData = faceData;
var consFunc;
consFunc = faceArr[faceData.sex];
face_mc = new consFunc;
face_mc.name = faceMcNameArr[0];
while (_head.numChildren!=0) {
_head.removeChildAt(numChildren - 1);
}
_head.addChild(face_mc);
for (var i:int = 1; i < faceMcNameArr.length; i++ ) {
var sprite:*;
if (faceData.sex == 0) {
consFunc = this["M" + this.faceList[i - 1]][faceData[faceDataList[i - 1]]];
sprite = new consFunc;
sprite.name = faceMcNameArr[i];
_head.addChild(sprite);
}else {
consFunc = this["F" + this.faceList[i - 1]][faceData[faceDataList[i - 1]]];
sprite = new consFunc;
sprite.name = faceMcNameArr[i];
_head.addChild(sprite);
}
}
/*//====
if(sex==0){
consFunc = MhairArr[faceData.hair];
hair_mc = new consFunc;
hair_mc.name = "hair_mc";
if (_head.getChildByName("hair_mc") == null)_head.addChild(hair_mc);
//----
consFunc = MhairArr[faceData.hair];
hair_mc = new consFunc;
hair_mc.name = "hair_mc";
if (_head.getChildByName("hair_mc") == null)_head.addChild(hair_mc);
}else {
consFunc = FhairArr[faceData.hair];
hair_mc = new consFunc;
hair_mc.name = "hair_mc";
if (_head.getChildByName("hair_mc") == null)_head.addChild(hair_mc);
}*/
}
//-----------------------------------------
protected function init() {
initBodyXY();
initPartMc();
initBodyPartNode();
}
protected function initPartMc(){
_body=this[BODY];
_head=_body[HEAD];
_body2=_body[BODY_2];
_rightHand=_body[RIGHT_HAND];
_leftHand=_body[LEFT_HAND];
_rightLeg=_body2[RIGHT_LEG];
_leftLeg=_body2[LEFT_LEG];
_rightHand2=_rightHand[RIGHT_HAND_2];
_leftHand2=_leftHand[LEFT_HAND_2];
_rightLeg2=_rightLeg[RIGHT_LEG_2];
_leftLeg2=_leftLeg[LEFT_LEG_2];
bodyPartMcArr = new Array(_body, _head, _rightHand, _rightHand2, _leftHand, _leftHand2, _rightLeg, _rightLeg2, _leftLeg, _leftLeg2, _body2);
for (var i:int = 0; i < bodyPartMcArr.length;i++ ) {
bodyPartMcArr[i].bodyContent = this;
}
}
protected function initBodyXY(){
this[BODY].x = CharactorData.BODY_X;
this[BODY].y = CharactorData.BODY_Y;
}
protected function initBodyPartNode(){
_body.controlled = null;
for (var i:int = 1; i < bodyPartMcArr.length;i++ ) {
_body.addControlsUnit(bodyPartMcArr[i]);
}
_head.controlled=_body.getBodyPartMc(Body.UP_NODE_MC);
_body2.controlled=_body.getBodyPartMc(Body.DOWN_NODE_MC);
_rightHand.controlled=_body.getBodyPartMc(Body.RIGHT_NODE_MC);
_leftHand.controlled=_body.getBodyPartMc(Body.LEFT_NODE_MC);
_rightLeg.controlled=_body2.getBodyPartMc(Body2.RIGHT_NODE_MC);
_leftLeg.controlled=_body2.getBodyPartMc(Body2.LEFT_NODE_MC);
_rightHand2.controlled=_rightHand.getBodyPartMc(HandLeg.NODE_MC);
_leftHand2.controlled=_leftHand.getBodyPartMc(HandLeg.NODE_MC);
_rightLeg2.controlled=_rightLeg.getBodyPartMc(HandLeg.NODE_MC);
_leftLeg2.controlled=_leftLeg.getBodyPartMc(HandLeg.NODE_MC);
}
}

}


关于木偶的执行效率
  整个木偶主要消耗资源的地方在字符串命令解析的地方,试验以前,我对as解析字符串,也就是查找字符串中命令的效果进行了测试,我用的字符串是从一个类似游戏数值的电子表格中取出来的,里面存放了金庸先生的14部武侠小说的所有武功秘籍和武器装备的游戏资料,当然是我自己填的,但是数据量相当庞大`` 如此巨大的文本,解析时间用了256毫秒… 看来as还是挺住了。不过也许这样的情况并不够极端,并不足以说明字符串解析的可行性。或许也可以考虑考虑使用正则表达式来做解析这部分的功能。不过无论如何,这个仅仅是种游戏角色的实现方案探讨性的试验,其可行性可能并不高,那么大家就权当是种小小的娱乐吧``
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值