Cocos Creator基础

222 阅读9分钟

1.基础知识

1.1.cocos与CocosCreator编程思想差异

65251064c458853aefa4af6b.jpg

652511e4c458853aefa50861.jpg

1.2.节点

创建节点

65252a58c458853aefad3a70.jpg

节点的属性设置

65265df7c458853aef7f5eec.jpg

652660d0c458853aef803a04.jpg

1.3.锚点与坐标系

锚点的设置

65266381c458853aef8119bb.jpg

65266419c458853aef814c57.jpg

65420c1dc458853aef025530.jpg

坐标系

65266a8cc458853aef84d961.jpg

65266b17c458853aef84f987.jpg

1.4.精灵的使用

65420c3cc458853aef02a801.jpg

九宫格

6538fc33c458853aef698fca.jpg

1.5.精灵图集

6540fb2bc458853aef1f8be0.jpg

6540fc57c458853aef246e3a.jpg

1.6.向量与标量

65420be5c458853aef01b45a.jpg

1.7.向量的运算及意义

65410084c458853aef37662e.jpg

65410360c458853aef43c8f1.jpg

6542238bc458853aef3d3926.jpg

1.8.脚本解析

65422c5fc458853aef539707.jpg

65422d01c458853aef5531fd.jpg

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  //@property能在面板上显示属性,括号里填写属性的类型能在面板显示属性类型
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";

  // LIFE-CYCLE CALLBACKS:

  // onLoad () {}

  start() {

  }

  // update (dt) {}
}

1.9.脚本生命周期

654225dbc458853aef42e7a2.jpg

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";

  // LIFE-CYCLE CALLBACKS:
  //初始化调用
  onLoad() {
    console.debug("onLoad");
  }

  onEnable() {
    console.debug("onEnable");
  }
    
  //初始化调用
  start() {
  }
    
  //每帧调用
  update (dt) {}
    
  lateUpdate() {}

  onDisable() {}
  //销毁时调用
  onDestroy() {
    console.debug("onDestroy");
  }

}

1.10.节点的常用属性方法

const { ccclass, property } = cc._decorator;

@ccclass
export default class Test extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";
    
  // LIFE-CYCLE CALLBACKS:
    
  start() {
    console.debug("start");
    // 获得子节点
    // this.node.children[0];
    // this.node.getChildByName("abc");
    // cc.find("Canvas/Main Camera");
    // 获得父节点
    // this.node.getParent();
    // this.node.setParent('ddd');
    // 移除所有节点
    // this.node.removeAllChildren();
    // 移除某个节点
    // this.node.removeChild(ddd);
    // 从父节点中移除掉
    // this.node.removeFromParent();

    // 访问位置
    // this.node.x;
    // this.node.y;
    // this.node.setPosition(3, 4);
    // this.node.setPosition(cc.v2(3, 4));
    // 旋转
    // this.node.rotation;
    // 缩放
    // this.node.scale;
    // 锚点
    // this.node.anchorX;
    // this.node.color = cc.Color.BLUE;

    // 节点开关
    // this.node.active = false;
    // 组件开关
    // this.enabled = false;

    // 获取组件
    // let sprite = this.getComponent(cc.Sprite);
    // this.getComponentInChildren(cc.Sprite);
  }

  update(dt) {}
}

1.11.预设体的使用

生成预设体

65424911c458853aefacc843.jpg

通过脚本创建节点、添加组件

    // 创建节点
    // let node = new cc.Node("new");
    // 添加组件
    // node.addComponent(cc.Sprite);

使用预设体

65424c34c458853aefb7e48d.jpg

const { ccclass, property } = cc._decorator;

@ccclass
export default class Test extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";

  // 预设体
  @property(cc.Prefab)
  pre: cc.Prefab = null;

  // LIFE-CYCLE CALLBACKS:
    
  // 初始化调用
  start() {
    // 实例化预设体
    let node = cc.instantiate(this.pre);
    // 设置父节点
    node.setParent(this.node);
  }
    
  update(dt) {}
}

1.12.资源动态加载

const { ccclass, property } = cc._decorator;

@ccclass
export default class Test extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";

  // LIFE-CYCLE CALLBACKS:
    
  // 初始化调用
  start() {
    let that = this;
    cc.loader.loadRes("test/land", cc.SpriteFrame, function (err, sp) {
      that.getComponent(cc.Sprite).spriteFrame = sp;
    });
      
    //加载图集
    cc.loader.loadRes(
      "test/1",
      cc.SpriteAtlas,
      function (err, atlas: cc.SpriteAtlas) {
        that.getComponent(cc.Sprite).spriteFrame =
          atlas.getSpriteFrame("bg_day");
      }
    );
  }
    
  update(dt) {}
}

1.13.场景管理

const { ccclass, property } = cc._decorator;

@ccclass
export default class Test extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";
    
  // LIFE-CYCLE CALLBACKS:
    
  // 初始化调用
  start() {
    //加载第二个场景
    cc.director.loadScene("game2", function () {
    //当前已经加载到新场景里了
     });

    // 预加载
    cc.director.preloadScene("game2", function () {
    //这个场景加载到内存了但是还没有用
    cc.director.loadScene("game2");
     });
    
    //添加常驻节点,切换场景不会被注销
    cc.game.addPersistRootNode(this.node);
    //移除常驻节点
    cc.game.removePersistRootNode(this.node);
  }
    
  update(dt) {}
}

1.14.键鼠事件

const { ccclass, property } = cc._decorator;

@ccclass
export default class Test extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";
    
  // LIFE-CYCLE CALLBACKS:
    
  // 初始化调用
  start() {
    // 鼠标事件
    this.node.on(cc.Node.EventType.MOUSE_DOWN, function (event) {
      console.log("鼠标按下了" + event.getLocation());
      if (event.getButton() == cc.Event.EventMouse.BUTTON_RIGHT) {
        console.log("右键");
      }
      if (event.getButton() == cc.Event.EventMouse.BUTTON_LEFT) {
        console.log("左键");
      }
    });

    // 全局键盘事件
    cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, function (event) {
      if (event.keyCode == cc.macro.KEY.w) {
        console.log("w");
      }
      if (event.keyCode == cc.macro.KEY.q) {
        console.log("q");
      }
    });
  }
    
  update(dt) {}
}

1.15.触摸与自定义事件

const { ccclass, property } = cc._decorator;

@ccclass
export default class Test extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";
    
  // LIFE-CYCLE CALLBACKS:
    
  // 初始化调用
  start() {
    // 触摸事件
    let self = this;
    this.node.on(cc.Node.EventType.TOUCH_START, (event) => {
      console.log("触摸" + event.getID());
      console.log("触摸" + event.getLocation());
      // self.node.emit("myEvent1");
      // true:是否冒泡
      this.node.dispatchEvent(new cc.Event.EventCustom("myEvent1", true));
    });

    this.node.on("myEvent1", function (event) {
      console.log("自定义事件");
    });
  }
    
  update(dt) {}
}

654309a9c458853aef74f3e1.jpg

1.16.碰撞检测

不基于物理性的碰撞

65430debc458853aef7f33f0.jpg

65431041c458853aef84c82f.jpg

在bg_day挂载脚本

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  @property(cc.Label)
  label: cc.Label = null;

  @property
  text: string = "hello";

  // LIFE-CYCLE CALLBACKS:

  // onLoad () {}

  start() {
    // 碰撞检测
    cc.director.getCollisionManager().enabled = true;
  }

  // 产生碰撞
  onCollisionEnter(other) {
    // tag用于辨识碰撞到的物体
    console.log("碰撞发生" + other.tag);
  }
  onCollisionStay(other) {
    console.log("碰撞持续");
  }
  onCollisionExit(other) {
    console.log("碰撞结束");
  }

  // update (dt) {}
}

1.17.音频播放

65431041c458853aef84c82f.jpg

654350d9c458853aef3ad816.jpg

以代码的方式播放音频

const { ccclass, property } = cc._decorator;

@ccclass
export default class AudioManager extends cc.Component {
  start() {
    // 组件的方式进行播放
    let player: cc.AudioSource = this.getComponent(cc.AudioSource);
    cc.loader.loadRes("海阔天空", cc.AudioClip, (res, clip) => {
      //赋值音频
      player.clip = clip;
      player.play();
      // 是否正在播放
      player.isPlaying;
      // 暂停
      player.pause();
      // 恢复
      player.resume();
      // 停止
      player.stop();
      // 循环
      player.loop = true;
      player.volume = 1;
    // });

    // 不依赖组件的播放方式
    cc.loader.loadRes("海阔天空", cc.AudioClip, (res, clip) => {
      // 播放
      let audioId: number = cc.audioEngine.playMusic(clip, true);
      // 是否正在播放
      cc.audioEngine.isMusicPlaying();
      // 暂停
      cc.audioEngine.pause(audioId);
      // 恢复
      cc.audioEngine.resume(audioId);
      // 停止
      cc.audioEngine.stop(audioId);
      // 循环
      cc.audioEngine.setLoop(audioId, true);
      // 声音大小
      cc.audioEngine.setVolume(audioId, 1);
    });
  }

  // update (dt) {}
}

1.18.物理系统

65435f68c458853aef629600.jpg

const { ccclass, property } = cc._decorator;

@ccclass
export default class BirdControl extends cc.Component {
  onLoad() {
    // 启用物理系统
    cc.director.getPhysicsManager().enabled = true;
  }
}

6543683bc458853aef7a4af0.jpg

1.19.物理碰撞

65437131c458853aef8d5ac2.jpg

654371bcc458853aef8e8a53.jpg

654374a7c458853aef95438b.jpg

碰撞检测

const { ccclass, property } = cc._decorator;

@ccclass
export default class BirdControl extends cc.Component {
  onLoad() {
    // 启用物理系统
    cc.director.getPhysicsManager().enabled = true;
  }

  start() {
    let rbody = this.getComponent(cc.RigidBody);
    //x轴y轴作用力 作用点 立刻运用
    // rbody.applyForce(cc.v2(1000, 0), cc.v2(0, 0), true);
    // rbody.applyForceToCenter(cc.v2(5000, 0), true);
    rbody.linearVelocity = cc.v2(50, 0);
  }

  onBeginContact(contact, self, other) {
    // 得到碰撞点
    let points = contact.getWorldManifold().points;
    // 法线
    let normal = contact.getWorldManifold().points;
    console.log("发生碰撞" + normal);
  }

  onEndContact(contact, self, other) {
    console.log("结束碰撞");
  }
}

1.20.射线

65438050c458853aefb030a0.jpg

65438216c458853aefb54edd.jpg

65438573c458853aefbe6fb5.jpg

const { ccclass, property } = cc._decorator;

@ccclass
export default class BirdControl extends cc.Component {
  onLoad() {
    // 启用物理系统
    cc.director.getPhysicsManager().enabled = true;

    let results = cc.director
      .getPhysicsManager()
      .rayCast(
        this.node.getPosition(),
        cc.v2(this.node.x, this.node.y + 100),
        cc.RayCastType.Closest
      );

    for (let i = 0; i < results.length; i++) {
      let res = results[i];
      //射线碰到的碰撞器
      //   res.collider;
      //碰到的点
      //   res.point;
      //碰到的法线
      //   res.normal;
    }
  }
}

1.21.动作系统

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  // onLoad () {}

  start() {
    // 动作
    // 移动到点
    let action = cc.moveTo(2, cc.v2(200, 200));
    // 移动多少
    action = cc.moveBy(2, cc.v2(200, 200));
    // 旋转
    action = cc.rotateTo(2, 100);
    // 缩放
    action = cc.scaleTo(2, 1.5, 0.5);
    // 跳跃
    action = cc.jumpBy(2, 200, 0, 100, 3);
    // 闪烁
    action = cc.blink(3, 5);
    // 淡出
    action = cc.fadeOut(3);
    // 淡入
    action = cc.fadeIn(3);
    // 渐变
    action = cc.fadeTo(3, 100);
    // 颜色
    action = cc.tintTo(3, 100, 30, 300);
    // 执行动作
    this.node.runAction(action);
    // 停止动作
    // this.node.stopAction(action);
    // this.node.stopAllActions();
    // action.setTag(33);
    // this.node.stopActionByTag(33);
    // this.node.pauseAllActions();
    // this.node.resumeAllActions();
  }
}

1.22.容器动作

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  // onLoad () {}

  start() {
    // 容器动作
    // 显示
    let action = cc.show();
    // 隐藏
    action = cc.hide();
    // 切换显示隐藏
    action = cc.toggleVisibility();
    // 翻转
    action = cc.flipX(true);
    action = cc.flipY(true);
    // 回调动作
    action = cc.callFunc(() => {});

    action = cc.fadeOut(1);
    let action2 = cc.fadeIn(1);
    // 队列/序列 动作
    let seq = cc.sequence(
      action,
      action2,
      cc.delayTime(1),
      cc.callFunc(() => {})
    );
    let repeat = cc.repeat(seq, 3);
    repeat = cc.repeatForever(seq);

    // 并列动作
    let move = cc.moveTo(3, 500, 500);
    let color = cc.tintTo(3, 100, 100, 20);
    let spawn = cc.spawn(move, color);
    this.node.runAction(spawn);
  }
}

1.23.动画系统

6543a7d4c458853aef3c5e88.jpg

6543a849c458853aef3e38a2.jpg

6543ae2dc458853aef570eac.jpg

6543ae2dc458853aef570eac.jpg

image.png

6543aeefc458853aef5a1b36.jpg

6543af3ec458853aef5b526d.jpg

GIF 2023-11-3 17-01-28.gif

1.24.动画曲线与事件

动画曲线

65474b0ec458853aefc749eb.jpg

65474b78c458853aefc8b5f8.jpg

事件

65474c61c458853aefcd067d.jpg

65474cb3c458853aefce5db6.jpg

65474d64c458853aefd0b48b.jpg

帧动画

65474fc2c458853aefd99ca2.jpg

GIF 2023-11-5 16-19-18.gif

代码控制播放动画

const { ccclass, property } = cc._decorator;

@ccclass
export default class PlayerControl extends cc.Component {
  // onLoad () {}

  start() {
    let ani = this.getComponent(cc.Animation);

    ani.play("run");
    ani.pause();
    ani.resume();
    ani.stop();
  }

  // update (dt) {}
}

1.25.屏幕Canvas

6548d2eec458853aef1c5165.jpg

label文本

6548d4b1c458853aef22d0d4.jpg

1.26.图文混排

RichText富文本

6548d58fc458853aef258acd.jpg

我是<color=#ff0000>红色</color>,<size=60>大</size>号字体,<i>斜体</i><u>下划线</u>,<color=#ff0000><outline>描边</outline></color>

点击事件

6548d62fc458853aef27718b.jpg

6548d656c458853aef27e925.jpg

图文混排

6548d704c458853aef2ac34a.jpg

1.27.屏幕适配与遮罩

遮罩

6548f168c458853aefae9700.jpg

6548f2f3c458853aefb93961.jpg

屏幕适配

6548f4cfc458853aefc63ab2.jpg

6548f67dc458853aefcece22.jpg

1.28.按钮与布局

6548f8c2c458853aefdeb907.jpg

654a346bc458853aef405a56.jpg

点击事件

654a3740c458853aef4cde3a.jpg

布局

654a3b3cc458853aef5ff5a7.jpg

654a3b57c458853aef609193.jpg

654a3c13c458853aef63cd86.jpg

1.29.滑动进度控件

654a4160c458853aef7d6433.jpg

654a433cc458853aef8626a5.jpg

654a434cc458853aef866b6c.jpg

654a43d3c458853aef89618c.jpg

1.30.输入框

654a46bac458853aef97827d.jpg

1.31.补充控件

654a4772c458853aef9aa1fc.jpg

654a4902c458853aefa4c579.jpg

654a49dcc458853aefab65bc.jpg

654a4a37c458853aefadd084.jpg

654a4ab7c458853aefb19a1b.jpg

1.32.数据存储

const { ccclass, property } = cc._decorator;

@ccclass
export default class DataTest extends cc.Component {
  start() {
    // 储存数据
    cc.sys.localStorage.setItem("name", "蜘蛛侠");
    // 获取数据
    let name = cc.sys.localStorage.getItem("name");
    // 移除一个数据
    cc.sys.localStorage.removeItem("name");
    // 清除
    cc.sys.localStorage.clear();
  }

  // update (dt) {}
}

1.33.Json数据

const { ccclass, property } = cc._decorator;

class Person {
  id: number;
  name: string;
  wugong: string[];
}

@ccclass
export default class JsonTest extends cc.Component {
  start() {
    /*
    json:
    数据格式:json xml csv 写文本
    客户端-客户端
    游戏存档 -> 地图,坐标,等级,攻击,防御,物品,武功,好友度......
    */
    let person: Person = new Person();
    person.id = 10;
    person.name = "李逍遥";
    person.wugong = ["降龙十八掌", "独孤九剑"];
    //对象->字符串
    /*
    json:{} 对象 []数组
    '{"id":10,"name":"李逍遥","wugong":["降龙十八掌", "独孤九剑"]}'
     */
    //对象->json 序列化
    let json = JSON.stringify(person);
    console.log(json);
    cc.sys.localStorage.setItem("save1", person);
    //json->对象
    let person2: Person = Object.assign(new Person(), JSON.parse(json));
    console.log(person2.name);
  }
}

1.34.数字格式

const { ccclass, property } = cc._decorator;

class Person {
  id: number;
  name: string;
  wugong: string[];
}

@ccclass
export default class JsonTest extends cc.Component {
  start() {
    //person[] ->字符串
    //json
    /* 
    [{
        "id":10,
        "name":"李逍遥",
        "wugong":[
            "降龙十八掌", "独孤九剑"
        ]
    },{
        "id":2,
        "name":"王小虎",
        "wugong":[
            "魔刀", "凌波微步"
        ]
    }]
    */
    //xml
    /*
        <root>
            <person id="10">
                <name>李逍遥</name>
                <wugong> 降龙十八掌,独孤九剑</wugong>
            </person>
            <person id="2">
                <name>王小虎</name>
                <wugong> 魔刀,凌波微步</wugong>
            </person>
        </root>
    */
    //csv
    /*
        10,李逍遥,降龙十八掌/独孤九剑
        2,王小虎,魔刀/凌波微步
    */
  }
}

1.35.网络请求

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  start() {
    let url = "http://iwenwiki.com:3000/search?keywords=海阔天空";
    // 请求
    let request = cc.loader.getXMLHttpRequest();
    request.open("GET", url, true); //异步 同步
    // 请求状态改变
    request.onreadystatechange = () => {
      // 请求结束后获取信息;
      if (request.readyState == 4 && request.status == 200) {
        console.log("请求完成");
        console.log(request.responseText);
      }
    };
    request.send();
  }

  // update (dt) {}
}

1.36.自定义Animation

654c3844c458853aef04007c.jpg

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  //每秒播放速度
  @property
  speed: number = 0.1;
  //播放帧数组
  @property(cc.SpriteFrame)
  sprites: cc.SpriteFrame[] = [];
  //是否播放动画
  @property
  isPlayer: boolean = false;
  //当前播放帧
  index: number = 0;
  //计时器
  timer: number = 0;

  start() {}

  play() {
    this.isPlayer = true;
  }

  stop() {
    this.isPlayer = false;
  }

  update(dt) {
    if (this.isPlayer) {
      //播放动画
      //计时器增加
      this.timer += dt;
      if (this.timer > this.speed) {
        this.timer = 0;
        this.index++;
        if (this.index >= this.sprites.length) {
          this.index = 0;
        }
        this.getComponent(cc.Sprite).spriteFrame = this.sprites[this.index];
      }
    }
  }
}

1.37.nodejs服务端

let app = require("express")();
let http = require("http").Server(app);
let io = require("socket.io")(http);

http.listen(3000, () => {
  console.log("server listen on 3000");
});

// 监听所有客户端连接
io.on("connection", (socket) => {
  // 发送消息
  //   socket.emit("message", "连接成功了");
  console.log("有客户端连接");
  // 监听客户端发来的消息
  socket.on("message", (data) => {
    console.log("客户端发来消息:" + data);
    socket.emit("message", "world");
  });
});

1.38.Socket.io客户端

const { ccclass, property } = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
  socket: Socket = null;

  start() {
    //连接服务器
    this.socket = io.connect("http://localhost:3000");
    // 判断是否连接成功
    this.socket.on("connect", (data) => {
      console.log("连接成功");
      this.socket.emit("message", "hello");

      //客户端接受消息
      this.socket.on("message", function (data) {
        console.log(data);
      });
    });

    //判断是否断开
    this.socket.on("disconnect", (data) => {});
  }

  // update (dt) {}
}

1.39.tielmap

654b46cbc458853aef660eab.jpg

654b57dfc458853aef9b2ea4.jpg

654b87e3c458853aef43130b.jpg

const { ccclass, property } = cc._decorator;

@ccclass
export default class MapCOntrol extends cc.Component {
  map: cc.TiledMap;
  player: cc.Node = null;

  start() {
    //获取地图信息
    this.map = this.getComponent(cc.TiledMap);
    //普通层
    // this.map.getLayer("item");
    //对象层
    let playerLayer = this.map.getObjectGroup("player");
    //获取某个对象
    let playerObj = playerLayer.getObject("startpos");
    //判断是否是玩家对象
    if (playerObj.isPlayer == true) {
      //加载玩家预设体
      cc.loader.loadRes("player", cc.Prefab, (res, playerPre) => {
        //创建玩家
        this.player = cc.instantiate(playerPre);
        this.player.setParent(this.node.children[2].children[0]);
        this.player.x = playerObj.x;
        this.player.y = playerObj.y;
      });
    }
  }

  update(dt) {
    //摄像头跟随玩家
    if (this.player != null) {
      cc.Camera.main.node.x = this.player.x - 240;
      cc.Camera.main.node.y = this.player.y - 160;
    }
  }
}

1.40.控制封装

654b89c3c458853aef4d0c6a.jpg

export default class Input {
  private static instance: Input = null;

  //水平轴
  horizontal: number = 0;
  //垂直轴
  vertical: number = 0;

  static get Instance() {
    if (this.instance == null) {
      this.instance = new Input();
    }
    return this.instance;
  }

  constructor() {
    //键盘按下
    cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, (event) => {
      if (event.KeyCode == cc.macro.KEY.w) {
        this.vertical = 1;
      } else if (event.KeyCode == cc.macro.KEY.s) {
        this.vertical = -1;
      }
      if (event.KeyCode == cc.macro.KEY.a) {
        this.vertical = -1;
      } else if (event.KeyCode == cc.macro.KEY.d) {
        this.vertical = 1;
      }
    });

    //键盘抬起
    cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, (event) => {
      if (event.KeyCode == cc.macro.KEY.w && this.vertical == 1) {
        this.vertical = 0;
      } else if (event.KeyCode == cc.macro.KEY.s && this.vertical == -1) {
        this.vertical = 0;
      }
      if (event.KeyCode == cc.macro.KEY.a && this.horizontal == -1) {
        this.vertical = 0;
      } else if (event.KeyCode == cc.macro.KEY.d && this.horizontal == 1) {
        this.vertical = 0;
      }
    });
  }
}
import Input from "./input";

const { ccclass, property } = cc._decorator;

@ccclass
export default class PlayerControl extends cc.Component {
  //速度
  speed: number = 20;

  start() {}

  update(dt) {
    this.node.x += this.speed * dt * Input.Instance.horizontal;
    this.node.y += this.speed * dt * Input.Instance.vertical;
  }
}

GIF 2023-11-8 21-28-14.gif

1.41.粒子系统

654b8f54c458853aef6a61b7.jpg

1.42.游戏框架

654b953cc458853aef86dbb3.jpg

// Message.ts
const { ccclass, property } = cc._decorator;

@ccclass
export default class Message {
  //类型 1 2 3
  Type: number;
  //命令 100 101 200 201
  Command: number;
  //参数
  Content: any;

  //构造函数
  constructor(type: number, command: number, content: any) {
    this.Type = type;
    this.Command = command;
    this.Content = content;
  }
}

export class MessageType {
  static Type_UI = 1;
  static Type_NPC = 2;
  static Type_Enemy = 3;
  static Type_Audio = 4;

  static UI_RefreshHp = 101;
  static UI_RefreshScore = 102;
  static UI_RefreshInventory = 103;

  static NPC_npc1 = 201;
  static NPC_npc2 = 202;

  static Enemy_enemy1 = 301;
  static Enemy_enemy2 = 302;
}
// ComponentBase.ts
import Message from "./Message";

const { ccclass, property } = cc._decorator;

@ccclass
export default class ComponentBase extends cc.Component {
  //接受消息
  ReceiveMessage(message: Message) {}
}
// ManagerBase.ts
import ComponentBase from "./ComponentBase";
import Message, { MessageType } from "./Message";
import MessageCenter from "./MessageCenter";

const { ccclass, property } = cc._decorator;

@ccclass
export default class ManagerBase extends ComponentBase {
  //管理的消息接受者数组
  ReceiveList: ComponentBase[] = [];
  //当前管理类接收的具体消息类型
  messageType: number;

  onLoad() {
    //设置当前管理类接收的消息类型
    this.messageType = this.SetMessageType();
    //把管理类添加到消息中心列表中
    MessageCenter.Managers.push(this);
  }

  //设置当前管理类的消息类型
  SetMessageType() {
    return MessageType.Type_UI;
  }

  //注册消息监听
  RegisterReceiver(cb: ComponentBase) {
    this.ReceiveList.push(cb);
  }

  ReceiveMessage(message: Message) {
    super.ReceiveMessage(message);
    //判断消息类型
    if (message.Type != this.messageType) {
      return;
    }
    //向下层分发消息
    for (let cb of this.ReceiveList) {
      cb.ReceiveMessage(message);
    }
  }
}
// MessageCenter.ts
import ComponentBase from "./ComponentBase";
import Message from "./Message";

export default class MessageCenter {
  //管理类列表
  static Managers: ComponentBase[] = [];

  //发送消息
  static SendMessage(msg: Message) {
    for (let manager of this.Managers) {
      manager.ReceiveMessage(msg);
    }
  }

  //发送自定义消息
  static SendCustomMessage(type: number, command: number, content: any) {
    let msg = new Message(type, command, content);
    this.SendMessage(msg);
  }
}

示例:

654b97e8c458853aef949c53.jpg

// PlayerControl.ts
import { MessageType } from "./Scripts/Message";
import MessageCenter from "./Scripts/MessageCenter";

const { ccclass, property } = cc._decorator;

@ccclass
export default class PlayerControl extends cc.Component {
  start() {
    //点击
    this.node.on(cc.Node.EventType.MOUSE_DOWN, (event) => {
      //血量减少
      MessageCenter.SendCustomMessage(
        MessageType.Type_UI,
        MessageType.UI_RefreshHp,
        10
      );
    });
  }

  // update (dt) {}
}
// UIManager.ts
import ManagerBase from "./Scripts/ManagerBase";
import { MessageType } from "./Scripts/Message";

const { ccclass, property } = cc._decorator;

@ccclass
export default class UIManager extends ManagerBase {
  static Instance: UIManager;

  onLoad() {
    super.onLoad();
    UIManager.Instance = this;
  }

  //接收消息的类型
  SetMessageType() {
    return MessageType.Type_UI;
  }
}
// HpControl.ts
import ComponentBase from "./Scripts/ComponentBase";
import Message, { MessageType } from "./Scripts/Message";
import UIManager from "./UIManager";

const { ccclass, property } = cc._decorator;

@ccclass
export default class HpControl extends ComponentBase {
  hp: number = 100;

  start() {
    //注册为ui的消息接收者
    UIManager.Instance.RegisterReceiver(this);
  }

  //接收到的消息
  ReceiveMessage(msg: Message) {
    super.ReceiveMessage(msg);
    if (msg.Command == MessageType.UI_RefreshHp) {
      //得到参数
      let num = <number>msg.Content;
      this.ChangeHp(num);
    }
  }

  //改变血量
  ChangeHp(attack) {
    this.hp -= attack;
    this.node.children[1].getComponent(cc.Label).string = this.hp + "";
  }
}

1.43.有限状态机

654b9919c458853aef997107.jpg

// FSMState.ts
import FSMManager from "./FSMManager";

const { ccclass, property } = cc._decorator;

export default class FSMState {
  //当前状态ID
  StateID: number;
  //状态拥有者
  component: cc.Component;
  //所属状态机
  fsmManager: FSMManager;

  constructor(
    stateID: number,
    component: cc.Component,
    fsmManager: FSMManager
  ) {
    this.StateID = stateID;
    this.component = component;
    this.fsmManager = fsmManager;
  }

  //进入状态
  OnEnter() {}

  //状态更新中
  OnUpdate() {}
}
// FSMManager.ts
import FSMState from "./FSMState";

const { ccclass, property } = cc._decorator;

export default class FSMManager {
  //状态列表
  StateList: FSMState[] = [];
  //当前状态ID
  CurrentIndex: number = -1;

  //改变状态
  ChangeState(StateID: number) {
    //改变状态ID
    this.CurrentIndex = StateID;
    //调用新状态的OnEnter方法
    this.StateList[this.CurrentIndex].OnEnter();
  }

  //更新调用
  OnUpdate() {
    if (this.CurrentIndex != -1) {
      //调用当前状态的update方法
      this.StateList[this.CurrentIndex].OnUpdate();
    }
  }
}

示例:

654b9a97c458853aefa0fe9a.jpg

// BirdControl.ts
import DieState from "./DieState";
import FlyState from "./FlyState";
import FSMManager from "./Scripts/FSMManager";

const { ccclass, property } = cc._decorator;

enum BirdState {
  Fly,
  Die,
}

@ccclass
export default class BirdControl extends cc.Component {
  ani: cc.Animation;
  //状态机
  fsmManager: FSMManager;

  start() {
    this.fsmManager = new FSMManager();
    this.ani = this.getComponent(cc.Animation);
    let fly = new FlyState(BirdState.Fly, this, this.fsmManager);
    let die = new DieState(BirdState.Die, this, this.fsmManager);
    this.fsmManager.StateList = [fly, die];
    //开始执行状态
    this.fsmManager.ChangeState(BirdState.Fly);
  }

  update(dt) {
    if (this.fsmManager.CurrentIndex != -1) {
      this.fsmManager.OnUpdate();
    }
  }

  fly() {
    this.fsmManager.ChangeState(BirdState.Fly);
  }

  die() {
    this.fsmManager.ChangeState(BirdState.Die);
  }
}
// FlyState.ts
import FSMState from "./Scripts/FSMState";

const { ccclass, property } = cc._decorator;

@ccclass
export default class FlyState extends FSMState {
  start() {}

  //进入状态
  OnEnter() {
    super.OnEnter();
    this.component.getComponent(cc.Animation).play("fly");
  }

  //状态更新中
  OnUpdate() {
    super.OnUpdate();
  }
}
// DieState.ts
import FSMState from "./Scripts/FSMState";

const { ccclass, property } = cc._decorator;

@ccclass
export default class DieState extends FSMState {
  start() {}

  //进入状态
  OnEnter() {
    super.OnEnter();
    this.component.getComponent(cc.Animation).play("die");
  }

  //状态更新中
  OnUpdate() {
    super.OnUpdate();
  }
}

状态机的一些应用方面

654b9b81c458853aefa5d875.jpg