最近开始做3D项目了,用了一段时间CocosCreator3.4.1版本,记录一些简单的功能实现
点击地图玩家移动
摄像机跟随
地面uv无限滚动
有了地面贴图,再新建一个材质文件,并把贴图拖入
在场景创建一个Plane,拖入材质,将主摄像机镜头调到合适的角度,前期场景工作就完成了。
通常游戏角色的移动都是固定的几种控制方式,比如虚拟摇杆移动,点击地面移动,本篇来实现点击地面移动。
「点击地面移动的过程中 有几个关键点:」
点击屏幕,检测到地面的目标位置
获取玩家角度,改变朝向
玩家匀速移动
具体实现方式:
点击屏幕触发事件,根据触摸点从摄像机发射一条射线,射线检测到地面(地面需要添加碰撞器)之后,返回我们需要的具体数据:
const outRay = new geometry.Ray();
this.mainCamera.screenPointToRay(e.getLocation().x, e.getLocation().y, outRay);
let isCol = PhysicsSystem.instance.raycastClosest(outRay);
// 获取射线返回的数据
let result = PhysicsSystem.instance.raycastClosestResult;
// 移动的目标位置
let targetPoint = result.hitPoint;
// 移动
tween(this.soldier)
.to(1, { position: targetPoint })
.start()
基本的点击移动就实现了,移动当然要转头了,所以我们根据目标位置和玩家当前位置求出玩家朝向:
// 玩家角色当前位置
let curSelfPoint = this.soldier.getPosition();
const quat_end = new Quat(); // 最终旋转四元数
const dirV3 = targetPoint.clone().subtract(curSelfPoint).normalize() // 前方向向量
const dir_up = new Vec3(0, 1, 0);
// 模型正好朝z轴方向
Quat.fromViewUp(quat_end, dirV3, dir_up); // 根据视口的前方向和上方向计算四元数
this.soldier.setRotation(quat_end);
再给个匀速移动就完成了,
let distance = targetPoint.clone().subtract(curSelfPoint).length();
let moveSpeed = 4;
let moveTime = distance / moveSpeed;
tween(this.soldier)
.to(moveTime, { position: targetPoint })
.start()
「摄像机跟随的关键点:」
计算玩家和摄像机的坐标差
根据坐标差每帧设置摄像机坐标
具体实现方式:
获取玩家和摄像机的坐标差:
/** 获取摄像机和玩家的坐标差值 */
private getTheCoordinateDifference() {
let cameraPos = this.mainCamera.node.getPosition();
let playerPos = this.soldier.getPosition();
this._differencePos = cameraPos.subtract(playerPos);
}
然后在uodate里每帧给摄像机坐标赋值:
// upadte中调用
/** 摄像机跟随玩家 */
private cameraFollowPlayer() {
let playerPos = this.soldier.getPosition();
this.mainCamera.node.setPosition(playerPos.add(this._differencePos));
}
通常我们在做无限的地图时会用2到3块地面循环滚动来实现,但是如果玩家是360度无限制移动的话,通常都需要进行复杂的计算地面循环,很麻烦,所以我最后选择了使用uv滚动来模拟;
「地面uv滚动的关键点:」
地面节点位置要实时跟玩家同步
计算移动方向
用代码更改材质属性
首先利用uv滚动模拟的话,地面一定要和玩家位置同步,即地面实时跟随玩家
/** 地面跟随玩家 */
private mapFollowPlayer() {
let playerPos = this.soldier.getPosition();
this.planeNode.setPosition(playerPos)
}
在玩家移动的代码中获取玩家移动的方向
let eulerAngles = this.soldier.eulerAngles;
let toQuat = new Quat();
// 欧拉角转四元数
let tempQuat = Quat.fromEuler(toQuat, eulerAngles.x, eulerAngles.y, eulerAngles.z);
// 移动的向量方向
let normalV3 = new Vec3(0, 0, 1);
// 旋转后的向量normalV3
Vec3.transformQuat(normalV3, normalV3, tempQuat);
// 保存玩家方向,用与地图uv滚动
this.curNormalV3 = normalV3;
// 打开uv滚动开关
this.uvScrollSwitch = true;
tween(this.soldier)
.to(moveTime, { position: targetPoint })
.call(() => {
// 关闭uv滚动
this.uvScrollSwitch = false;
})
.start()
然后我们修改地面节点材质的tilingOffset属性,可以看到地面的纹理发生了变化,我们用代码每帧给tilingOffset赋值 根据玩家的方向计算uv偏移方向
代码:
/** 地图UV滚动 */
private mapUVScroll(dt) {
// 滚动开关
if (!this.uvScrollSwitch) return;
// 获取当前偏移数值
let curUV: any = this.planeNode.getComponent(MeshRenderer).material.getProperty("tilingOffset");
// 玩家朝向
let curNormalV3 = this.curNormalV3;
// 模拟移动速度数值,根据需要调整数值大小
let coeNum1 = 0.5;
let coeNum2 = 1.5;
// 给材质属性赋值
this.planeNode.getComponent(MeshRenderer).material.setProperty(
"tilingOffset",
curUV.add(new Vec4(0, 0, dt * coeNum1 / coeNum2 * curNormalV3.x, dt * coeNum1 / coeNum2 * curNormalV3.z)),
0
)
}
,时长00:31
!!公众号回复“uv”获取完整代码!!
Cocos从零开发一个翻译插件
更多精彩欢迎关注微信公众号
“点赞“ ”在看” 鼓励一下
▼