日式RPG角色跟随实现

不是第一次写这个东西了。

不是第一次发相关的帖了。

:smile:

时隔一年,有点小空,再次打开了cocos。可惜发现以前的代码看不懂了…


重新写了角色跟随,发现自己思路清析了,代码更简单了。留个印记。

效果图
image

  1. 在队长node挂上Player.ts脚本

本脚本主要接收用户输入(键盘事件)—可以优化成触摸方向盘。
控制 moveDir 的值,使队长往某方向移动
移动时发射HeroMove事件,通知队长的Hero.ts脚本

  1. 在队长、队员node 挂Hero.ts脚本。

实现Node的移动
follow followNode 注意两个值 一般是脚本设置,为了测试,直接在编辑器中拖Node
给队长的followNode设置小弟1 给小弟1的followNode设置小弟2 …
那么 小弟2就会跟随小弟1跟随队长移动了
注意小弟们与队长的层级关系


上代码

Player.ts

import FollowCamera from "./FollowCamera";

const { ccclass, property } = cc._decorator;

@ccclass
export default class Player extends cc.Component {

    moveDir: cc.Vec3 = cc.Vec3.ZERO;
    timer: number = 0;
    timerTotal: number = 1 / 100;//1代表1秒执行一次
    speed: number = 4;//速度

    followCamera: FollowCamera;

    onLoad() {
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);

    }

    start() {
        this.followCamera = cc.Camera.main.getComponent(FollowCamera);
        if (this.followCamera) {
            this.followCamera.setTarget(this.node);
        }
    }
    onKeyDown(event: cc.Event.EventKeyboard) {
        switch (event.keyCode) {
            case cc.macro.KEY.w:
                this.moveDir = cc.Vec3.UP
                break;
            case cc.macro.KEY.s:
                this.moveDir = cc.Vec3.UP.neg()
                break;
            case cc.macro.KEY.a:
                this.moveDir = cc.Vec3.RIGHT.neg()
                break;
            case cc.macro.KEY.d:
                this.moveDir = cc.Vec3.RIGHT
                break;
        }
    }

    onKeyUp(event: cc.Event.EventKeyboard) {
        switch (event.keyCode) {
            case cc.macro.KEY.w:
            case cc.macro.KEY.s:
            case cc.macro.KEY.a:
            case cc.macro.KEY.d:
                this.moveDir = cc.Vec3.ZERO;
                this.node.emit("HeroMoveStop");
                break;
        }
    }

    update(dt) {
        if (this.moveDir.equals(cc.Vec3.ZERO)) return;
        this.timer += dt;
        if (this.timer >= this.timerTotal) {
            this.timer = 0;
            this.move()
        }
    }

    move() {
        var v3 = this.node.position.add(this.moveDir.mul(this.speed));
        //TODO 判断是否可以移动到v3 地型检测
        var obj: HeroMoveObj = {
            position: v3,
            dir: this.moveDir
        };
        this.node.emit("HeroMove", obj);
    }

}

Hero.ts

import Helper from "./Lib/Helper";

const { ccclass, property } = cc._decorator;

@ccclass
export default class Hero extends cc.Component {



    @property
    heroName: string = "刘备";

    CSprite: cc.Sprite;
    CAnimation: cc.Animation;


    paths: HeroMoveObj[] = [];//移动路径
    @property
    pathLength: number = 30;//路径存储长度
    /**
     * 跟随者使用本英雄移动路径存储中的第N个位置 
     * 12这个值是图块大小32像素除以移动速度4* 3/2得到的一个适合的值
    */
    @property
    pathDiff: number = 12;


    /**
     * 跟随在屁股后面的人
     * 应该是脚本中设置 在开发中拖放方便 先定义一个临时变量
     */
    follow: Hero;

    @property(cc.Node)
    followNode: cc.Node



    onLoad() {
        this.CSprite = this.getComponent(cc.Sprite);
        this.CAnimation = this.getComponent(cc.Animation);

        cc.resources.load("role/" + Helper.getRoleSpriteNameForName(this.heroName), cc.SpriteAtlas, (err, atlas: cc.SpriteAtlas) => {
            var sprites = atlas.getSpriteFrames();
            this.CSprite.spriteFrame = sprites[0];

            for (let name of Helper.dirNames) {
                var clip = cc.AnimationClip.createWithSpriteFrames(sprites.splice(0, 3), 6)
                clip.name = name;
                clip.wrapMode = cc.WrapMode.Loop;

                this.CAnimation.addClip(clip, name)
            }
        })
        this.node.on("HeroMove", this.heroMove, this);
        this.node.on("HeroMoveStop", this.heroMoveStop, this);


        this.paths.push({
            position: this.node.position,
            dir: cc.Vec3.UP
        });

    }

    start() {
        if (this.followNode) {
            this.follow = this.followNode.getComponent(Hero);
        }
    }

    // update (dt) {}


    heroMove(obj: HeroMoveObj) {

        if (!this.CAnimation) {
            return false;
        }


        this.node.position = obj.position;
        this.paths.push(obj);



        //通知跟随都移动到当前英雄的移动路径中的第n个位置
        if (this.follow) {
            var length = this.paths.length;
            var index = 0;
            if (length >= this.pathDiff) {
                index = length - this.pathDiff;
            }
            this.follow.heroMove(this.paths[index]);
        }

        if (this.paths.length > this.pathLength) {
            this.paths.shift();
        }



        var name = Helper.getDirForVec3(obj.dir);
        if (!this.CAnimation.currentClip || this.CAnimation.currentClip.name != name) {
            this.CAnimation.play(name);
        } else {
            var state = this.CAnimation.getAnimationState(name);
            if (state.isPaused) {
                this.CAnimation.playAdditive(name);
            }
        }
    }

    heroMoveStop() {
        this.CAnimation.pause();
        if (this.follow) {
            this.follow.heroMoveStop();
        }
    }


}

4赞

你好,这个能给个demo嘛?

没做过 不过我觉得这个应该很简单 每个角色的移动其实都是执行的第一个角色的移动 所以只要定义一个指令集 第一个角色每成功移动一下 就往指令集里添加一个移动的指令 后续角色按照顺序执行指令集里的指令就行了

主要思路就是这个

贪吃蛇就是这个思路

嗯,简单升级到 3.6.2

https://gitee.com/yeshao2069/CocosCreatorDemos/tree/v3.6.x/demo/2dP1/Creator3.6.2_2D_RoleFollow