<三>制作第一个3D游戏(进阶)
这一篇接着完善demo。
添加光照
光影是描述游戏的重要渲染特性,通过光源和阴影可以模拟更加真实的游戏世界,提供更好的沉浸感和代入感。
在创建项目时,场景默认带了一个挂载了 cc.DirectionalLight 组件的平行光 Main Light 节点。
虽然有光源,但运行时发现并没有阴影,阴影需要另外开启。
开启阴影
1.在层级管理器中选中场景节点,勾选Shadow组件上的Enable属性。

2.在 层级管理器 中选中 Light,然后在 属性检查器 的 Dynamic Shadow Settings 组件中勾选 Shadow Enabled 属性。
3.在 层级管理器 中选中需要显示阴影的 3D 节点,然后在 属性检查器 的 MeshRenderer 组件中将 ShadowCastingMode 属性设置为 ON。
按步骤开启阴影后发现运行时还是看不到阴影,应该是阴影的位置不对。
可以调整这个平行光的方向,让阴影的显示换个位置。

更换主角模型
到Cocos商城找一个免费又好看的人物模型作为新的主角模型。

将它拖拽到 层级管理器 中 Player 节点下的 Body 节点中,作为 Body 节点的子节点
移除Body上的胶囊体模型。

卡哇伊!~~~
优化动画
更换的萌妹模型有10个动画剪辑,其中就有Idle动画和Jump动画。这里需要把原来用Tween实现的动画逻辑给优化一下。
先查看一下动画:
选中资源管理器中的模型文件FBX

发现idle01和jump02都可以用,但jump02花里胡哨的动作太多了,只想要向上跳起的动作,所以需要重新剪辑一下。

修改后的PlayerCtrl
import { _decorator, Component, EventMouse, Input, input, Node, SkeletalAnimation, tween, v3, Vec3 } from 'cc';
import { EventMgr } from './EventMgr';
import { EventName } from './Enum';
const { ccclass, property } = _decorator;
@ccclass('PlayerController')
export class PlayerController extends Component {
@property(SkeletalAnimation)
player_Anim: SkeletalAnimation = null;
private _isJumping: boolean = false;//是否跳跃中
private _targetPos: Vec3 = v3();//目标位置
private _curPos: Vec3 = v3();//当前位置
private _jumpStep: number = 0;//跳跃步长,一步还是两步,根据步长计算目标位置
private _curJumpTime: number = 0;//当前跳跃时间
private _jumpTime: number = 0.5;//跳跃持续时间/每次跳跃的时长,匹配移动时长
private _moveSpeed: number = 0;//移动速度
private _deltaPos: Vec3 = v3();//每帧移动的距离
private _curMoveStep: number = 0;//记录当前移动步数
start() {
//输入监听
//input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
}
reset() {
this._curPos = v3();
this._targetPos = v3();
this._isJumping = false;
this._jumpStep = 0;
this._curJumpTime = 0;
this._moveSpeed = 0;
this._deltaPos = v3();
this._curMoveStep = 0;
}
/**
* 是否开启关闭输入
* @param active
*/
setInputActive(active: boolean) {
if (active) {
input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);
} else {
input.off(Input.EventType.MOUSE_UP, this.onMouseUp, this);
}
}
onMouseUp(event: EventMouse) {
//如果是鼠标左键,getButton 方法会返回 0,而如果是右键,则返回 2
if (event.getButton() == 0) {
//这里不要直接给_jumpStep赋值,因为需要判断是否正在跳跃中,否则会多次调用
this.jumpByStep(1);
} else if (event.getButton() == 2) {
this.jumpByStep(2);
}
}
/**
* 辅助计算跳跃移动的信息(目标位置,跳跃时间等等),实现跳跃动画
* @param step
* @returns
*/
jumpByStep(step: number) {
if (this._isJumping) {
//跳跃是一个完整的步骤,如果正在跳跃中不做任何响应
return;
}
this._isJumping = true;
this._jumpStep = step;
//跳跃动画
// tween(this.node.getChildByName("Body"))
// .to(this._jumpTime * 0.5, { position: v3(0, this._jumpStep * 0.5, 0) })
// .to(this._jumpTime * 0.5, { position: v3(0, 0, 0) })
// .start();
this.player_Anim.getState("jump03").speed = 1.5;//设置动画速度(1.5)
this.player_Anim.play("jump03");
this._curJumpTime = 0;//重置跳跃时间
this._moveSpeed = this._jumpStep / this._jumpTime;//计算移动速度
this.node.getPosition(this._curPos);//获取当前位置
Vec3.add(this._targetPos, this._curPos, v3(this._jumpStep, 0, 0));//计算目标位置
this._curMoveStep += step;//记录移动步数
}
/**
* 每一次跳跃结束回调
*/
onOnceJumpEnd() {
this.player_Anim.play("idle01");
EventMgr.emit(EventName.JumpEnd, this._curMoveStep);//发射一次跳跃结束事件
}
update(deltaTime: number) {
if (!this._isJumping) return;//如果没有跳跃,则不处理任何跳跃移动的逻辑
this._curJumpTime += deltaTime;//计算当前跳跃时间
if (this._curJumpTime >= this._jumpTime) {//如果已经完成一次完整的跳跃,则结束跳跃
this.node.setPosition(this._targetPos);//强制逐句移动到目标位置
this._isJumping = false;
this.onOnceJumpEnd();
} else {
this.node.getPosition(this._curPos);//获取当前位置
this._deltaPos.x = this._moveSpeed * deltaTime;//计算每帧移动的距离
Vec3.add(this._curPos, this._curPos, this._deltaPos);//计算当前位置
this.node.setPosition(this._curPos);//逐帧移动到当前位置
}
}
}
显示步长 && 用时

创建结算界面

其他更多的可能都是添加一些UI,不再赘述,后面慢慢再补充吧。
浙公网安备 33010602011771号