用Typescript在Cocos Creator里实现。
第一种实现方式
import { _decorator, Component, EventKeyboard, Input, input, KeyCode, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Controller')
export class Controller extends Component {
//移动速度
@property
public moveSpeed: number = 100;
//移动方向
private _moveDirection: Vec3 = new Vec3(0, 0, 0);
protected onLoad(): void {
//注册键盘事件监听
input.on(Input.EventType.KEY_DOWN, this._onKeyDown, this);
input.on(Input.EventType.KEY_UP, this._onKeyUp, this);
}
protected onDestroy(): void {
//注销键盘事件监听
input.off(Input.EventType.KEY_DOWN, this._onKeyDown, this);
input.off(Input.EventType.KEY_UP, this._onKeyUp, this);
}
private _onKeyDown(event: EventKeyboard) {
//只要事件发生,就会给_moveDirection添加方向
switch (event.keyCode) {
case KeyCode.KEY_W:
this._moveDirection.y = 1;
break;
case KeyCode.KEY_S:
this._moveDirection.y = -1;
break;
case KeyCode.KEY_A:
this._moveDirection.x = -1;
break;
case KeyCode.KEY_D:
this._moveDirection.x = 1;
break;
}
}
private _onKeyUp(event: EventKeyboard) {
//只要事件发生,_moveDirection就会清零
switch (event.keyCode) {
case KeyCode.KEY_W:
case KeyCode.KEY_S:
this._moveDirection.y = 0;
break;
case KeyCode.KEY_A:
case KeyCode.KEY_D:
this._moveDirection.x = 0;
break;
}
}
protected update(deltaTime: number) {
let movements = new Vec3(this._moveDirection.x * this.moveSpeed * deltaTime, this._moveDirection.y * this.moveSpeed * deltaTime, 0);
this.node.setPosition(this.node.position.add(movements));
}
}
这是最简单的实现方式,然而这种方式在键盘上的操作手感却是比较诡异的。比如,当按住了A键时,玩家操控的对象向左移动,此时不要松开A键按下D键,玩家操控的对象向右移动,然而在松开A键或D键的时候,明明移动的按键还在按着,玩家操控的对象却定住不动了。其实仔细阅读代码可以发现,这是因为当玩家松开按键的时候触发了_onKeyUp,使得_moveDirection.x清零了,然而此时由于A键一直是按住的状态,并没有触发_onKeyDown,所以_moveDirection.x的值一直是0。
不过如果用的是手柄或者摇杆,由于左右键或上下键并不能同时按下,就不会产生这样的诡异现象。所以考虑键盘玩家,就需要对控制代码进行修改。
第二种实现方式
import { _decorator, Component, EventKeyboard, Input, input, KeyCode, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Controller')
export class Controller extends Component {
//移动速度
@property
public moveSpeed: number = 100;
//移动方向
private _moveDirection: Vec3 = new Vec3(0, 0, 0);
//添加标志位,分开记录方向键的按下状态
private _isUpPressed: boolean = false;
private _isDownPressed: boolean = false;
private _isLeftPressed: boolean = false;
private _isRightPressed: boolean = false;
protected onLoad(): void {
//注册键盘事件监听
input.on(Input.EventType.KEY_DOWN, this._onKeyDown, this);
input.on(Input.EventType.KEY_UP, this._onKeyUp, this);
}
protected onDestroy(): void {
//注销键盘事件监听
input.off(Input.EventType.KEY_DOWN, this._onKeyDown, this);
input.off(Input.EventType.KEY_UP, this._onKeyUp, this);
}
private _onKeyDown(event: EventKeyboard) {
//只要事件发生,就改变标志位的状态为true
switch (event.keyCode) {
case KeyCode.KEY_W:
this._isUpPressed = true;
break;
case KeyCode.KEY_S:
this._isDownPressed = true;
break;
case KeyCode.KEY_A:
this._isLeftPressed = true;
break;
case KeyCode.KEY_D:
this._isRightPressed = true;
break;
}
//在有按键按下的时候,更新_moveDirection
this._updateMoveDirection();
}
private _onKeyUp(event: EventKeyboard) {
//只要事件发生,就改变标志位的状态为false
switch (event.keyCode) {
case KeyCode.KEY_W:
this._isUpPressed = false;
break;
case KeyCode.KEY_S:
this._isDownPressed = false;
break;
case KeyCode.KEY_A:
this._isLeftPressed = false;
break;
case KeyCode.KEY_D:
this._isRightPressed = false;
break;
}
//在有按键松开的时候,更新_moveDirection
this._updateMoveDirection();
}
protected update(deltaTime: number) {
let movements = new Vec3(this._moveDirection.x * this.moveSpeed * deltaTime, this._moveDirection.y * this.moveSpeed * deltaTime, 0);
this.node.setPosition(this.node.position.add(movements));
}
private _updateMoveDirection() {
//根据按键的状态,更新_moveDirection的值
this._moveDirection.x = (this._isLeftPressed? -1 : 0) + (this._isRightPressed? 1 : 0);
this._moveDirection.y = (this._isUpPressed? -1 : 0) + (this._isDownPressed? 1 : 0);
}
}
这种方式就解决了松开按键时,还在按着的按键失灵的情况。但这种实现方式有个特点,就是当A键和D键同时按下,或者当W键和S键同时按下时,玩家操控的对象会静止不动,有一点不太灵活,如果想要添加灵活性,希望玩家在按下A键的时候按下D键,操控的对象向右移动,此时松开D键,操控的对象回归向左移动的状态,那怎么实现呢。
第三种实现方式
import { _decorator, Component, EventKeyboard, Input, input, KeyCode, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
//记录方向的枚举
enum Direction {
UP,
DOWN,
LEFT,
RIGHT
}
@ccclass('Controller')
export class Controller extends Component {
//移动速度
@property
public moveSpeed: number = 100;
//移动方向
private _moveDirection: Vec3 = new Vec3(0, 0, 0);
//添加标志位,分开记录方向键的按下状态
private _isUpPressed: boolean = false;
private _isDownPressed: boolean = false;
private _isLeftPressed: boolean = false;
private _isRightPressed: boolean = false;
//添加标志位,记住最后按下的方向
private _lastVerticalDirection: Direction.UP | Direction.DOWN = null;
private _lastHorizontalDirection: Direction.LEFT | Direction.RIGHT = null;
protected onLoad(): void {
//注册键盘事件监听
input.on(Input.EventType.KEY_DOWN, this._onKeyDown, this);
input.on(Input.EventType.KEY_UP, this._onKeyUp, this);
}
protected onDestroy(): void {
//注销键盘事件监听
input.off(Input.EventType.KEY_DOWN, this._onKeyDown, this);
input.off(Input.EventType.KEY_UP, this._onKeyUp, this);
}
private _onKeyDown(event: EventKeyboard) {
//只要事件发生,就改变标志位的状态为true,以及改变_lastVerticalDirection和_lastHorizontalDirection以记录最后按下的方向
switch (event.keyCode) {
case KeyCode.KEY_W:
this._isUpPressed = true;
this._lastVerticalDirection = Direction.UP;
break;
case KeyCode.KEY_S:
this._isDownPressed = true;
this._lastVerticalDirection = Direction.DOWN;
break;
case KeyCode.KEY_A:
this._isLeftPressed = true;
this._lastHorizontalDirection = Direction.LEFT;
break;
case KeyCode.KEY_D:
this._isRightPressed = true;
this._lastHorizontalDirection = Direction.RIGHT;
break;
}
//在有按键按下的时候,更新_moveDirection
this._updateMoveDirection();
}
private _onKeyUp(event: EventKeyboard) {
//只要事件发生,就改变标志位的状态为false
switch (event.keyCode) {
case KeyCode.KEY_W:
this._isUpPressed = false;
if(this._isDownPressed){
this._lastVerticalDirection = Direction.DOWN;
}else{
this._lastVerticalDirection = null;
}
break;
case KeyCode.KEY_S:
this._isDownPressed = false;
if(this._isUpPressed){
this._lastVerticalDirection = Direction.UP;
}else{
this._lastVerticalDirection = null;
}
break;
case KeyCode.KEY_A:
this._isLeftPressed = false;
if(this._isRightPressed){
this._lastHorizontalDirection = Direction.RIGHT;
}else{
this._lastHorizontalDirection = null;
}
break;
case KeyCode.KEY_D:
this._isRightPressed = false;
if(this._isLeftPressed){
this._lastHorizontalDirection = Direction.LEFT;
}else{
this._lastHorizontalDirection = null;
}
break;
}
//在有按键松开的时候,更新_moveDirection
this._updateMoveDirection();
}
protected update(deltaTime: number) {
let movements = new Vec3(this._moveDirection.x * this.moveSpeed * deltaTime, this._moveDirection.y * this.moveSpeed * deltaTime, 0);
this.node.setPosition(this.node.position.add(movements));
}
private _updateMoveDirection() {
//根据_lastVerticalDirection和_lastHorizontalDirection的值,更新_moveDirection的值
if(this._lastVerticalDirection === Direction.UP){
this._moveDirection.y = 1;
}else if(this._lastVerticalDirection === Direction.DOWN){
this._moveDirection.y = -1;
}else{
this._moveDirection.y = 0;
}
if(this._lastHorizontalDirection === Direction.LEFT){
this._moveDirection.x = -1;
}else if(this._lastHorizontalDirection === Direction.RIGHT){
this._moveDirection.x = 1;
}else{
this._moveDirection.x = 0;
}
}
}
这种实现方式看似完美解决了第二种实现方式不够灵活的状态,但其实也有很多的具体问题,甚至在格斗游戏和射击游戏这两个品类上掀起过不小的风波。这也是我通过一个视频了解到的。
所以不同的游戏还是需要根据具体的实际情况去设计按键逻辑。