Role control
After the map and character are created, you can use the mouse, keyboard, or instructions from the server to run, fight Wait for the animation action. If you simply control the execution of the animation directly, there will be another one playing before the action is finished. Multiple operations will only execute the last one. Make the animation look very chaotic and uncontrollable
At this time, we need to add a set of state to the animation, and then design a set of mechanism that can issue a command - > execute a command according to the State - > change the State - > the command is executed. In this way, the animation looks very natural, and then the control is relatively simple
Role
To facilitate control, we further encapsulate some data such as roleobject and roledata
/** * role */ class Role implements IPooledObject { protected _roleData:RoleData;//Role data protected _roleObject:RoleObject;//Character display object protected _pauseState:boolean = false; protected _pauseObject:boolean = false; protected _playRate:number = 1; protected _controller:ActionStateController;//Action state controller protected _actionCommandProcessor:ActionCommandProcessor;// Action command processor protected _roleActionListener:IRoleActionListener;//Role action monitor interface protected _roleActionCommandListener:IRoleActionCommandListener;//Role action command listener interface protected _roleMapListener:IRoleMapListener;
Role status
The status is as follows, which can be adjusted according to the needs of the game
NONE = 0, none
IDLE = 1, idle
MOVE = 2, move
ATTACK = 3, attack
BE_ATTACK = 4, attacked
DEADING = 5, dying
DEAD = 6, dead
COLLECT = 7, collect
ACTIVE = 8, active
How to handle state
/** * state */ public get state():RoleActionState { return this._state; } /** * Get whether it is the current state */ protected isCurrentState():boolean { return this.roleData.isInState(this._state); } /** * Is it in the current state */ public isInState(state:RoleActionState):boolean { return this.state == state; } /** * Whether the previous state is the specified state */ public isInStateLast(state:RoleActionState):boolean { return this.lastState == state; } /** * Whether the status can be set */ public canSetState(state:RoleActionState):boolean { return this.state != state; } /** * Set status */ public setState(state:RoleActionState):boolean { if (this.state != state) { if (this.state != RoleActionState.NONE && this.state != RoleActionState.IDLE) { this.lastNoneEmptyState = this.state; } this.lastState = this.state; this.state = state; return true; } else { this.lastState = this.state; return false; } } /** * Cancel status or not */ public canCancelState(state:RoleActionState):boolean { return this.state == state; } /** * Cancel status */ public cancelState(state:RoleActionState):boolean { if (this.state == state) { this.setState(RoleActionState.NONE); return true; } return false; } /** * Is it idle */ public isIdleState():boolean { return this.state == RoleActionState.NONE || this.state == RoleActionState.IDLE; }
Perform animation
command
Command is the process of changing the execution of a character's actions on a server or player,
Command classification
The command is set to correspond to the action of the animation. Move has move command, attack has attack command
Command processor
It is used to manage commands, add and remove commands, stop commands, clean commands, etc
/** * Action command processor */ class ActionCommandProcessor implements IActionCommandListener { public role:Role; public currentCommand:IActionCommand; protected _nextCommands:Array<IActionCommand>; public constructor(role:Role) { this.role = role; this._nextCommands = new Array<IActionCommand>(); } /** * Whether the action command is being executed */ public get isCommandRunning():boolean { return this.currentCommand != null; } /** * Include action command or not */ public hasCommand(clazz:any):boolean { if (this.currentCommand != null && this.currentCommand instanceof clazz) { return true; } return this.hasDelayCommand(clazz); } /** * Include delay command or not */ public hasDelayCommand(clazz:any):boolean { for (var command of this._nextCommands) { if (command instanceof clazz) { return true; } } return false; } /** * Remove delay command */ public removeDelayCommand(clazz:any):boolean { var result:boolean = false; for (var i = this._nextCommands.length - 1; i >= 0; i--) { if (this._nextCommands[i] instanceof clazz) { this._nextCommands.splice(i, 1); result = true; } } return result; } /** * Start command execution */ public startCommand(command:IActionCommand, isDelay:boolean = false):boolean { if (this.currentCommand != null) { if (isDelay) { this._nextCommands.push(command); } return false; } this.currentCommand = command; this.currentCommand.role = this.role; this.currentCommand.setListener(this); this.currentCommand.start(); return true; } /** * Stop command execution */ public stopCommand(immediate:boolean = false):void { if (this.currentCommand != null) { if (this.currentCommand.canStop == false && immediate == false) { return; } this.clearNextCommands(); this.currentCommand.stop(immediate); if (immediate) { this.currentCommand = null; } } } /** * List of cleanup commands */ protected clearNextCommands():void { for (var command of this._nextCommands) { ObjectPoolManager.inst.releaseObject(command); } this._nextCommands.length = 0; } /** * to update */ public update(deltaTime:number):void { if (this.currentCommand != null) { this.currentCommand.update(deltaTime); } } /** * Remove target */ public removeTarget(target:Role):void { if (this.currentCommand != null) { this.currentCommand.removeTarget(target); } } /** * Action command complete */ public onActionCommandComplete(command:IActionCommand):void { if (this.currentCommand == command) { this.role.onActionCommandComplete(this.currentCommand); ObjectPoolManager.inst.releaseObject(command); this.currentCommand = null; if (this._nextCommands.length > 0) { var tmpCommand:IActionCommand = this._nextCommands.shift(); this.startCommand(tmpCommand); } } else { ObjectPoolManager.inst.releaseObject(command); } } /** * Reset */ public reset():void { this.clearNextCommands(); if (this.currentCommand != null) { ObjectPoolManager.inst.releaseObject(this.currentCommand); this.currentCommand = null; } } }
Command execution
The core logic of specific execution of each command is to create corresponding action data and then execute the action
public start():void { super.start(); if (this.position != null) { GameModules.role.setRolePosition(this.role, this.position); } var attackAction:AttackAction = ObjectPoolManager.inst.getObject(AttackAction) as AttackAction; attackAction.skillResID = this.skillResID; attackAction.direction = this.direction; attackAction.targets = this.targets; attackAction.targetPositions = this.targetPositions; attackAction.position = this.position; attackAction.destPosition = this.destPosition; attackAction.moveTime = this.moveTime; attackAction.animationLoop = this.animationLoop; attackAction.actionCallback = this._finishCallback; attackAction.onlyAction = this.onlyAction; this.doAction(attackAction); }
Action data is basically divided by action
Action execution
Action classification
In command this.doAction Because the action involves playing speed, direction, zoom A lot of different needs of processing. So for different needs of the implementation of action design action classification
Action control
Similar to the command processor, it controls the switching of actions
/** * Update controller */ public update(deltaTime:number):void { if (this._currentController != null) { this._currentController.update(deltaTime); } else if (this._defaultController != null) { this._defaultController.update(deltaTime); } } /** * Enable controller */ public activateController(state:RoleActionState, action:IRoleAction, ignoreState:boolean = false):void { var controller:IRoleActionController = this._controllerDict[state]; if (controller != null) { if (controller.activate(action, ignoreState)) { if (this._currentController != null && this._currentController != controller) { this._currentController.deactivateImmediately(); } this._currentController = controller; } } } /** * Disable controller */ public deactivateController(state:RoleActionState):void { var controller:IRoleActionController = this._controllerDict[state]; if (controller != null) { if (controller.deactivate()) { if (this._currentController == controller) { this._currentController = null; } } } }
Action execution
In short, it's OK to play the animation directly in the action class
protected activateInner(action:IRoleAction):boolean { this._moveAction = action as MoveAction; if (this._moveAction != null) { this._maxTime = this._moveAction.moveTime; // calculation speed this.caculateMoveSpeed(); // Set roleData this.roleData.direction = this._moveAction.direction; this.roleData.destPosition.copyFrom(this._moveAction.destPosition); // Play action this.roleObject.playAction(this._moveAction.action, this._moveAction.direction); this.roleObject.frameIndex = this.roleData.moveFrame; // Set transparency if (GameModules.map.getTransparentByMapPosition(this._moveAction.destPosition)) { this.roleObject.setAlpha(RoleSetting.ROLE_ALPHA_TRANSPARENT); } else { this.roleObject.setAlpha(RoleSetting.ROLE_ALPHA_NORMAL); } return true; } else { return false; } } protected caculateMoveSpeed():void { this._speedX = (this._moveAction.destPosition.realX - this.roleObject.position.x) / this._moveAction.moveTime; this._speedY = (this._moveAction.destPosition.realY - this.roleObject.position.y) / this._moveAction.moveTime; } protected updateInner(deltaTime:number):void { if (this.isMaxTime()) { this.roleObject.setPositionXYZ(this._moveAction.destPosition.realX, this._moveAction.destPosition.realY); } else { this.roleObject.setPositionOffsetXYZ(this._speedX * deltaTime, this._speedY * deltaTime); } if (this._role.roleMapListener != null) { this._role.roleMapListener.onRolePositionChanged(this._role, this.roleObject.position.x, this.roleObject.position.y); } }
This code is a mobile animation playback, from the previous chapters know
this.roleObject.playAction (this._ moveAction.action , this._ moveAction.direction ); it is the real play sequence frame animation
The logic of other places involved
Transparency of roles
this.roleObject.setAlpha
Role position changes on the map
this.roleObject.setPositionXYZ
For example, playing attack involves skill effect, which is more complex. So it is necessary to do action processing. For complex attacks, the next chapter is expected to talk about the realization of attack skill effect