2024年10月NJSD技术盛典暨第十届NJSD软件开发者大会、第八届IAS互联网架构大会在南京召开。百度文心快码总经理臧志分享了《AI原生研发新范式的实践与思考》,探讨了大模型赋能下的研发变革及如何在公司和行业中落地,AI原生研发新范式的内涵和推动经验。
作为大龄程序员,也接触不少游戏了,比较喜欢角色扮演、塔防策略之类的,比如植物大战僵尸、保卫萝卜突然萌发了想自己做一款类似游戏的想法,虽然有些服务端编程经验,但是从没有涉及过游戏开发领域但是都4202年了,智能游戏开发时代,谁还从头学起啊,说干就干
环境搭建
1)Cocos Creator 场景编辑器在 Cocos Creator 中新建一个空的2D项目,并等待初始化完成
2)VS Code 脚本编辑器 + 文心快码在 VS Code 打开刚创建的项目,以便于后续脚本开发
地图移动与缩放
在游戏场景中,可以根据相对运动的原理来实现地图的移动和缩放(山不向我走来,我便向山走去--默罕默德),即一种是控制地图进行位置、大小的转换,另一种则是控制视角的变化,主要是通过调整摄像机参数来完成的。接下来我们使用控制地图的方式。1)找一张地图素材并添加到Cocos Creator的资源管理器中,然后在当前场景的画布下创建一个精灵节点,并将贴图设置为刚添加的地图文件
2)需要设置触发的区域,所以添加了一个空节点作为触发源,其大小和位置与当前画布一致,同时还添加了一个TS脚本用于移动和缩放逻辑的实现
3)回到 VS Code,打开 Comate 侧边栏,输入个简单的需求描述先试试效果
> 快码快码,请帮我写一个cocos3.8的ts脚本,实现对地图节点的移动和缩放,通过鼠标进行操作< 下面是一个简单的示例,展示了如何在Cocos Creator3.8中使用TypeScript脚本来实现对地图节点的移动和缩放。这个脚本应该被附加到你想要控制的地图节点上。import { _decorator, Component, Node, Event, Vec2, SystemEvent, Enum } from 'cc';const { ccclass, property } = _decorator;@ccclass('MapController')export class MapController extends Component { @property({ type: Node }) public mapNode!: Node; // 假设这是你想要移动和缩放的地图节点 @property({ type: Number, default: 0.1 }) public moveSpeed: number = 0.1; // 移动速度 @property({ type: Number, default: 0.01 }) public scaleSpeed: number = 0.01; // 缩放速度 private isDragging: boolean = false; // 是否正在拖动 private lastMousePos: Vec2 = new Vec2(); // 上一次鼠标位置 start() { // 注册鼠标事件 this.node.on(Node.EventType.MOUSE_DOWN, this.onMouseDown, this); this.node.on(Node.EventType.MOUSE_MOVE, this.onMouseMove, this); this.node.on(Node.EventType.MOUSE_UP, this.onMouseUp, this); this.node.on(Node.EventType.MOUSE_WHEEL, this.onMouseWheel, this); } onDestroy() { // 注销鼠标事件 this.node.off(Node.EventType.MOUSE_DOWN, this.onMouseDown, this); this.node.off(Node.EventType.MOUSE_MOVE, this.onMouseMove, this); this.node.off(Node.EventType.MOUSE_UP, this.onMouseUp, this); this.node.off(Node.EventType.MOUSE_WHEEL, this.onMouseWheel, this); } onMouseDown(event: Event.EventMouse) { this.isDragging = true; this.lastMousePos = event.getLocation(); } onMouseMove(event: Event.EventMouse) { if (this.isDragging) { let delta = event.getLocation().sub(this.lastMousePos); this.lastMousePos = event.getLocation(); let pos = this.mapNode.position; pos.x += delta.x this.moveSpeed; pos.y += delta.y this.moveSpeed; this.mapNode.setPosition(pos); } } onMouseUp(event: Event.EventMouse) { this.isDragging = false; } onMouseWheel(event: Event.EventMouse) { let delta = event.getScrollY(); // 滚轮滚动的值 let scale = this.mapNode.scale; scale.x += delta this.scaleSpeed; scale.y += delta this.scaleSpeed; // 限制缩放范围,防止过度缩放 scale.x = Math.max(0.1, Math.min(5, scale.x)); scale.y = Math.max(0.1, Math.min(5, scale.y)); this.mapNode.setScale(scale); } update(deltaTime: number) { // 这里可以添加其他更新逻辑,如果需要的话 }}4)调试及运行感觉第一次生成的效果就很不错,看起来像模像样,又尝试调整了几次,函数定义、鼠标对应事件注册,内部逻辑都不缺,而且最关键的还把待触发的目标对象节点、移动速度、缩放速度都暴露为了外部属性,不得不说是一个小惊喜。从 Comate 中采纳到文件中后,VS Code 编辑器提示了一些语法错误,不知道是不是少安装了什么其他插件。另外根据Cocos Creator 3.8官方教程的一些示例来看,一些写法也有些区别。可能是版本兼容的问题,先调试着看吧。接着回到场景编辑器,将地图节点关联到地图控制节点的属性上,该属性就来自脚本中刚定义的MapNode属性。
不出意外的话,意外就该发生了。果然第一次运行就报错了,碰到了未定义的向量 sub 方法
5)查了下官方文档,这是个向量类型,正确的方法名应为subtract。在脚本编辑器中进行修改后,再次点击预览按钮查看更改后的效果。这次成功了,地图移动效果、缩放效果实现的非常正确。
敌军还有三秒到达战场
1)接下来是希望能出现一个敌人,可以沿着地图上的小路自上而下进攻。没有实现思路,继续请教文心快码
> 快码快码,请问cocos中,如何实现让物体沿着指定路径移动< 在 Cocos Creator 中,要实现物体沿着指定路径移动,你可以创建一个路径点数组,然后使用插值或者路径跟随算法来让物体沿着这些点移动。以下是一个基本的步骤指南:定义路径: 首先,你需要定义物体将要遵循的路径。这通常是通过创建一个包含多个坐标点的数组来完成的。每个坐标点代表路径上的一个位置。插值计算: 使用插值(如线性插值或贝塞尔曲线插值)来计算物体在路径上的当前位置。你可以根据时间或步数来更新物体的位置。更新物体位置: 在每一帧或每个时间步中,根据插值计算的结果更新物体的位置。循环或终止条件: 设置条件以判断物体是否到达路径的终点,并决定是否让物体循环移动或停止。下面是一个简化的 Cocos Creator 脚本示例,展示了如何实现这个功能:cc.Class({ extends: cc.Component, properties: { path: [cc.Vec2], // 在编辑器中指定路径点 speed: 0, // 移动速度 currentIndex: 0, // 当前路径点的索引 }, start() { this.moveToNextPoint(); }, moveToNextPoint() { if (this.currentIndex >= this.path.length) { this.currentIndex = 0; // 循环到路径起点,或者设置为-1以停止移动 } let startPosition = this.node.position; let targetPosition = cc.v2(this.path[this.currentIndex]); let action = cc.moveTo(2, targetPosition).easing(cc.easeInOutQuad); this.node.runAction(action.completed(() => { this.currentIndex++; this.moveToNextPoint(); })); }, update(dt) { // 可以在这里添加其他逻辑,如根据路径点更新朝向等 },});在这个示例中,path 属性是一个包含多个 cc.Vec2 对象的数组,表示路径上的点。moveToNextPoint 函数使用 Cocos Creator 的动作系统来移动节点到下一个路径点。当动作完成时,它会递增 currentIndex 并递归调用自身来移动到下一个点。请注意,这个示例是简化的,并且可能需要根据你的具体需求进行调整。例如,你可能需要调整移动速度、添加错误处理、考虑节点的朝向等。此外,如果你想要更复杂的路径跟随效果,比如让物体沿着曲线平滑移动,你可能需要使用更高级的插值方法或者路径跟随算法。2)继续询问文心快码,如何在cocos中画一个路径出来
快码快码,请问在cocos中,如何画一个移动路径< 在Cocos Creator中,要画一个移动路径并不可见地在场景中展示出来,你可以通过以下几种方法来实现:方法 1: 使用图形渲染组件(Graphics)Cocos Creator 提供了一个图形渲染组件(Graphics),允许你在场景中动态绘制图形,包括线段、曲线等。你可以使用这个组件来绘制你的移动路径。在场景编辑器中创建一个新节点,并添加一个 Graphics 组件。在脚本中,获取到这个 Graphics 组件的引用,并使用其 API 来绘制路径。例如,你可以使用 moveTo()、lineTo() 和 stroke() 方法来绘制线段。方法 2: 使用预制体(Prefab)或节点作为路径点在场景编辑器中,手动创建一系列节点,每个节点代表路径上的一个点。调整这些节点的位置,以定义你的移动路径。(可选)你可以将这些节点转换为一个预制体(Prefab),以便在多个场景或实例中重用。在脚本中,你可以通过引用这些节点来获取路径点的位置,并据此移动你的游戏对象。方法 3: 使用路径编辑工具(如Tilemap或其他第三方插件)Cocos Creator 社区可能有提供路径编辑的插件或工具,你可以搜索并尝试使用这些工具来更方便地编辑和定义移动路径。3)简单起见,考虑使用方法二,首先定一个动画节点,然后沿着地图的道路通过关键帧来定义移动的坐标
4)接下来就是在脚本中通过动态的方式获取该节点的位置数组
快码快码,cocos中,如何获取Animation中的关键帧,以及position的信息< 在Cocos Creator中,如果你想获取Animation中的关键帧以及对应的position信息,通常需要通过访问动画剪辑(AnimationClip)中的曲线(Curve)数据。由于Cocos Creator的API可能会随着版本更新而有所变化,以下是一个基本的方法,用于从AnimationClip中获取关键帧及其position信息:获取AnimationClip: 首先,你需要获取到包含动画的AnimationClip实例。这通常可以通过在资源管理器中加载动画资源或直接在场景中引用动画组件来实现。访问曲线数据: AnimationClip中包含了动画的所有曲线数据,包括位置(position)、旋转(rotation)、缩放(scale)等。你可以通过AnimationClip的curves属性来访问这些曲线。解析关键帧: 每条曲线都由一系列的关键帧组成,这些关键帧包含了时间和值的信息。你可以遍历曲线的关键帧,获取每个关键帧的时间和对应的值(在position曲线的情况下,这个值就是一个表示位置的向量)。5)接下来就是把上述的步骤用代码串起来,都交给文心快码。
快码快码,请帮我写一个cocos3.8的ts脚本,让当前对象沿着RoadAnim中定义的路径移动< 在Cocos Creator 3.8中,如果你想要让一个对象沿着Animation组件中定义的路径移动,你通常会通过动画编辑器来创建关键帧动画,然后在代码中控制动画的播放。不过,Cocos Creator的动画系统不直接支持路径动画,所以你需要通过关键帧来定义路径上的点,并让对象在更新时根据动画的进度移动到相应的位置。以下是一个简单的TypeScript脚本示例,它展示了如何在Cocos Creator 3.8中使用Animation组件来让当前对象沿着在动画编辑器中定义的路径移动import { _decorator, Component, Node, Animation, AnimationState, Vec2 } from 'cc';const { ccclass, property } = _decorator;@ccclass('MoveController')export class MoveController extends Component { @property(Animation) RoadAnim: Animation = null; private animState: AnimationState = null; private isPlaying: boolean = false; private positionKeys: { frame: number, value: Vec2 }[] = []; // 存储位置关键帧 start() { if (this.RoadAnim) { // 假设动画已经在动画编辑器中创建并设置了位置关键帧 this.initPositionKeys(); // 初始化位置关键帧数据 this.playAnimation(); // 播放动画 } } update(deltaTime: number) { if (this.isPlaying && this.animState) { let currentTime = this.animState.currentTime; let position = this.getPositionAtTime(currentTime); // 根据当前时间获取位置 this.node.setPosition(position); // 设置对象位置 if (this.animState.isDone()) { this.isPlaying = false; // 动画播放完毕,停止更新位置 } } } private initPositionKeys() { // 这里需要根据你的动画设置来提取位置关键帧,通常这需要在动画编辑器中手动设置并导出数据 // 以下为示例数据,实际情况需要根据你的动画进行调整 this.positionKeys = [ { frame: 0, value: new Vec2(0, 0) }, { frame: 1, value: new Vec2(100, 0) }, { frame: 2, value: new Vec2(100, 100) }, // ... 更多关键帧数据 ]; } private playAnimation() { this.animState = this.RoadAnim.play(); // 播放动画 this.isPlaying = true; // 设置动画播放标志 this.animState.wrapMode = cc.WrapMode.Loop; // 设置动画循环播放,如果需要的话 } private getPositionAtTime(currentTime: number): Vec2 { // 根据动画的当前时间,插值计算对象的位置 // 这里使用简单的线性插值,你可以根据需要使用更复杂的插值方法 let prevKey = this.positionKeys[0]; for (let i = 1; i < this.positionKeys.length; i++) { let key = this.positionKeys[i]; if (currentTime < key.frame) { let t = (currentTime - prevKey.frame) / (key.frame - prevKey.frame); return Vec2.lerp(prevKey.value, key.value, t); // 线性插值计算位置 } prevKey = key; } return this.positionKeys[this.positionKeys.length - 1].value; // 如果超出最后一个关键帧,返回最后一个关键帧的位置 }}6)调试及运行从生成结果看,主要框架逻辑比较完整,而且还增加了一个插值计算的功能,用于平滑移动的动画效果,可以说是非常意外了。但小问题还是有一些,可以人工解决:缺少从AnimateClip中获取关键节点及坐标属性的代码获取Animate组件状态的方式有误,应该是版本兼容的问题,Cocos 3.8不支持该写法代码中主要使用二维向量处理坐标,但Cocos 3.8官方建议使用三维向量,以避免一些问题在快码生成的基础上修复后的代码import { _decorator, Component, Node, Animation, AnimationState, Vec2 } from 'cc';const { ccclass, property } = _decorator;@ccclass('MoveController')export class MoveController extends Component { @property(Animation) RoadAnim: Animation = null; private animState: AnimationState = null; private isPlaying: boolean = false; private positionKeys: { frame: number, value: Vec2 }[] = []; // 存储位置关键帧 start() { if (this.RoadAnim) { // 假设动画已经在动画编辑器中创建并设置了位置关键帧 this.initPositionKeys(); // 初始化位置关键帧数据 this.playAnimation(); // 播放动画 } } update(deltaTime: number) { if (this.isPlaying) { this.animState = this.RoadAnim.getState('road'); let currentTime = this.animState.time / this.animState.duration * 10; let position = this.getPositionAtTime(currentTime); // 根据当前时间获取位置 this.node.setPosition(position.x, position.y, 0) if (!this.animState.isPlaying) { this.isPlaying = false; // 动画播放完毕,停止更新位置 } } } private initPositionKeys() { // 这里需要根据你的动画设置来提取位置关键帧,通常这需要在动画编辑器中手动设置并导出数据 // 以下为示例数据,实际情况需要根据你的动画进行调整 this.positionKeys = []; const position = this.RoadAnim.clips[0].tracks[0]; for (let i = 0; i < 10; i++) { const x = position.channels()[0].curve.getKeyframeValue(i).value; const y = position.channels()[1].curve.getKeyframeValue(i).value; this.positionKeys.push({ frame: i, value: new Vec2(x, y) }); } } private playAnimation() { this.animState = this.RoadAnim.play("road"); // 播放动画 this.isPlaying = true; // 设置动画播放标志 // this.animState.wrapMode = cc.WrapMode.Loop; // 设置动画循环播放,如果需要的话 } private getPositionAtTime(currentTime: number): Vec2 { // 根据动画的当前时间,插值计算对象的位置 // 这里使用简单的线性插值,你可以根据需要使用更复杂的插值方法 let prevKey = this.positionKeys[0]; for (let i = 1; i < this.positionKeys.length; i++) { let key = this.positionKeys[i]; if (currentTime < key.frame) { let t = (currentTime - prevKey.frame) / (key.frame - prevKey.frame); let out = new Vec2(0, 0); Vec2.lerp(out, prevKey.value, key.value, t); // 线性插值计算位置 return out; } prevKey = key; } return this.positionKeys[this.positionKeys.length - 1].value; // 如果超出最后一个关键帧,返回最后一个关键帧的位置 }}
感受
作为非常初级的小白游戏开发用户,感觉未来智能编码助手在游戏开发方面的帮助,其潜力是非常大的,主要体现在:各种资源、JSON配置与代码知识同等重要:开发过程大致是先在场景编辑器中摆放各种场景元素,设置表现效果(样式、动作)、然后关联脚本实现逻辑开发。在脚本编辑器中又会引用到场景元素进行指定操作。是一个双向关联的关系。对游戏引擎/平台框架的知识的增强,对于各种常用效果,即使是简单的移动、旋转、缩放,都需要编写大量的代码;或者对于一些想法,受限于开发者经验或者对游戏引擎的熟悉程度,不知道该怎么实现。以游戏场景为单位进行向量知识的组织:各种文件资源非常多且碎片化,稍微大一些的改动会引发一系列的查找、调试、测试,缺陷发生时排查定位起来也比较麻烦。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。