新增CameraController,现在项目可以正常运作了。
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "UIWorld",
|
||||
"_name": "UIGame",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "UIWorld",
|
||||
"_name": "UIGame",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
@@ -8,6 +8,6 @@
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "UIWorld"
|
||||
"syncNodeName": "UIGame"
|
||||
}
|
||||
}
|
||||
@@ -259,7 +259,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Plane",
|
||||
"_name": "Game",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -412,7 +412,7 @@
|
||||
"_priority": 1073741824,
|
||||
"_fov": 45,
|
||||
"_fovAxis": 0,
|
||||
"_orthoHeight": 360,
|
||||
"_orthoHeight": 412.3341946597761,
|
||||
"_near": 1,
|
||||
"_far": 2000,
|
||||
"_color": {
|
||||
|
||||
138
client/assets/scripts/App/Game/CameraController.ts
Normal file
138
client/assets/scripts/App/Game/CameraController.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { Camera, Component, Node, Vec3, _decorator } from 'cc';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* CameraController 摄像机控制器
|
||||
* 负责让摄像机跟随指定的目标物体
|
||||
*/
|
||||
@ccclass('CameraController')
|
||||
export class CameraController extends Component {
|
||||
/** 跟随目标 */
|
||||
private target: Node = null;
|
||||
|
||||
/** 摄像机节点 */
|
||||
private cameraNode: Node = null;
|
||||
|
||||
/** 摄像机组件 */
|
||||
private camera: Camera = null;
|
||||
|
||||
/** 相对偏移量 */
|
||||
private offset: Vec3 = new Vec3(0, 10, 8);
|
||||
|
||||
/** 跟随速度 */
|
||||
@property({ displayName: '跟随速度' })
|
||||
public followSpeed: number = 5;
|
||||
|
||||
/** 是否平滑跟随 */
|
||||
@property({ displayName: '平滑跟随' })
|
||||
public smoothFollow: boolean = true;
|
||||
|
||||
onLoad() {
|
||||
this.findMainCamera();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找主摄像机
|
||||
*/
|
||||
private findMainCamera(): void {
|
||||
// 查找名为 "Main Camera" 的摄像机
|
||||
const mainCameraNode = this.node.scene.getChildByName('Main Camera');
|
||||
if (mainCameraNode) {
|
||||
this.cameraNode = mainCameraNode;
|
||||
this.camera = mainCameraNode.getComponent(Camera);
|
||||
console.log('[CameraController] 找到主摄像机:', mainCameraNode.name);
|
||||
} else {
|
||||
console.error('[CameraController] 未找到主摄像机(Main Camera)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置跟随目标
|
||||
* @param target 跟随的目标节点
|
||||
*/
|
||||
public setTarget(target: Node): void {
|
||||
this.target = target;
|
||||
|
||||
if (this.target && this.cameraNode) {
|
||||
// 立即设置初始位置
|
||||
this.updateCameraPosition(false);
|
||||
console.log('[CameraController] 设置跟随目标:', target.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机偏移量
|
||||
* @param offset 相对于目标的偏移量
|
||||
*/
|
||||
public setOffset(offset: Vec3): void {
|
||||
this.offset.set(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前跟随目标
|
||||
*/
|
||||
public getTarget(): Node {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取摄像机节点
|
||||
*/
|
||||
public getCameraNode(): Node {
|
||||
return this.cameraNode;
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
if (this.target && this.cameraNode) {
|
||||
this.updateCameraPosition(this.smoothFollow, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新摄像机位置
|
||||
* @param smooth 是否使用平滑跟随
|
||||
* @param deltaTime 帧时间(smooth为true时需要)
|
||||
*/
|
||||
private updateCameraPosition(smooth: boolean = true, deltaTime: number = 0): void {
|
||||
if (!this.target || !this.cameraNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算目标位置
|
||||
const targetPosition = new Vec3();
|
||||
Vec3.add(targetPosition, this.target.worldPosition, this.offset);
|
||||
|
||||
if (smooth && deltaTime > 0) {
|
||||
// 平滑跟随
|
||||
const currentPosition = this.cameraNode.worldPosition;
|
||||
const newPosition = new Vec3();
|
||||
Vec3.lerp(newPosition, currentPosition, targetPosition, this.followSpeed * deltaTime);
|
||||
this.cameraNode.setWorldPosition(newPosition);
|
||||
} else {
|
||||
// 立即跟随
|
||||
this.cameraNode.setWorldPosition(targetPosition);
|
||||
}
|
||||
|
||||
// 让摄像机看向目标
|
||||
this.cameraNode.lookAt(this.target.worldPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止跟随
|
||||
*/
|
||||
public stopFollow(): void {
|
||||
this.target = null;
|
||||
console.log('[CameraController] 停止跟随');
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁控制器
|
||||
*/
|
||||
onDestroy(): void {
|
||||
this.target = null;
|
||||
this.cameraNode = null;
|
||||
this.camera = null;
|
||||
console.log('[CameraController] 摄像机控制器已销毁');
|
||||
}
|
||||
}
|
||||
9
client/assets/scripts/App/Game/CameraController.ts.meta
Normal file
9
client/assets/scripts/App/Game/CameraController.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b94f3dcb-3630-4ebd-87b3-81ae3d560352",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { _decorator, Component, Node, EventKeyboard, KeyCode, Input, input, Vec3 } from 'cc';
|
||||
import { NetManager } from '../../Framework/Net/NetManager';
|
||||
import { ReqMove, ResMove } from '../../Shared/protocols/PtlMove';
|
||||
import { PlayerInfo } from '../../Shared/protocols/PtlLogin';
|
||||
import { _decorator, Component, EventKeyboard, Input, input, KeyCode, Vec3 } from 'cc';
|
||||
import { RoleController } from '../../CC/RoleController';
|
||||
import { NetManager } from '../../Framework/Net/NetManager';
|
||||
import { PlayerInfo } from '../../Shared/protocols/MsgResLogin';
|
||||
import { MoveMessagePair } from '../Msg/Pair/MoveMessagePair';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@@ -134,7 +134,7 @@ export class PlayerController extends Component {
|
||||
private move(dt: number): void {
|
||||
// 计算移动增量
|
||||
const moveOffset = this.moveDirection.clone().multiplyScalar(this.moveSpeed * dt);
|
||||
|
||||
|
||||
// 更新节点位置
|
||||
const currentPos = this.node.position.clone();
|
||||
currentPos.add(moveOffset);
|
||||
@@ -163,7 +163,7 @@ export class PlayerController extends Component {
|
||||
*/
|
||||
private checkSendMoveRequest(currentPos: Vec3): void {
|
||||
const distance = Vec3.distance(currentPos, this.lastSentPosition);
|
||||
|
||||
|
||||
// 如果移动距离超过阈值,发送移动请求
|
||||
if (distance >= this.moveSendThreshold) {
|
||||
this.sendMoveRequest(currentPos.x, currentPos.z);
|
||||
@@ -177,9 +177,9 @@ export class PlayerController extends Component {
|
||||
private async sendMoveRequest(x: number, z: number): Promise<void> {
|
||||
try {
|
||||
const netManager = NetManager.getInstance();
|
||||
const result = await netManager.callApi<ReqMove, ResMove>('Move', {
|
||||
x: x,
|
||||
y: z
|
||||
const result = await netManager.callMsg(new MoveMessagePair(), {
|
||||
x: Math.round(x * 1000),
|
||||
y: Math.round(z * 1000)
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
|
||||
@@ -11,6 +11,7 @@ App/Game/
|
||||
├── World.ts # 世界管理器,管理所有玩家
|
||||
├── PlayerController.ts # 本地玩家控制器,处理输入和移动
|
||||
├── RemotePlayer.ts # 远程玩家类,处理其他玩家的显示和同步
|
||||
├── CameraController.ts # 摄像机控制器,跟随本地玩家
|
||||
├── UIGame.ts # 游戏主界面UI
|
||||
└── README.md # 本文档
|
||||
```
|
||||
@@ -82,7 +83,37 @@ await World.getInstance().init(worldRoot, playerInfo);
|
||||
- `updatePosition(position)`: 更新远程玩家位置(收到 MsgPlayerMove 时调用)
|
||||
- `destroy()`: 销毁远程玩家
|
||||
|
||||
### 4. UIGame (游戏主界面)
|
||||
### 4. CameraController (摄像机控制器)
|
||||
|
||||
**职责:**
|
||||
- 自动查找场景中的主摄像机(Main Camera)
|
||||
- 让摄像机跟随指定的目标物体(通常是本地玩家)
|
||||
- 提供平滑跟随和即时跟随两种模式
|
||||
- 自动让摄像机朝向目标
|
||||
|
||||
**主要特性:**
|
||||
- **自动查找**: 自动在场景中查找名为"Main Camera"的摄像机节点
|
||||
- **平滑跟随**: 使用线性插值实现平滑的摄像机跟随效果
|
||||
- **偏移设置**: 可配置摄像机相对于目标的偏移量(默认: Y+10, Z+8)
|
||||
- **朝向目标**: 摄像机会自动朝向跟随目标
|
||||
|
||||
**主要方法:**
|
||||
- `setTarget(target)`: 设置跟随目标节点(本地玩家)
|
||||
- `setOffset(offset)`: 设置摄像机相对偏移量
|
||||
- `stopFollow()`: 停止跟随
|
||||
- `getTarget()`: 获取当前跟随目标
|
||||
- `getCameraNode()`: 获取摄像机节点
|
||||
|
||||
**属性配置:**
|
||||
- `followSpeed`: 跟随速度(默认: 5)
|
||||
- `smoothFollow`: 是否启用平滑跟随(默认: true)
|
||||
|
||||
**使用说明:**
|
||||
- CameraController 由 World 管理器自动创建
|
||||
- 只有本地玩家会绑定摄像机跟随,远程玩家不会
|
||||
- 摄像机控制器会挂载到世界根节点上
|
||||
|
||||
### 5. UIGame (游戏主界面)
|
||||
|
||||
**职责:**
|
||||
- 游戏主界面 UI 组件
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
import { _decorator } from 'cc';
|
||||
import { UIBase } from '../../Framework/UI/UIBase';
|
||||
import { World } from './World';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -10,8 +9,6 @@ const { ccclass, property } = _decorator;
|
||||
*/
|
||||
@ccclass('UIGame')
|
||||
export class UIGame extends UIBase {
|
||||
@property(Node)
|
||||
worldRoot: Node = null;
|
||||
|
||||
protected onLoad(): void {
|
||||
console.log('[UIGame] onLoad');
|
||||
@@ -33,13 +30,7 @@ export class UIGame extends UIBase {
|
||||
* 获取 UI 预制体路径
|
||||
*/
|
||||
public onGetUrl(): string {
|
||||
return 'res://UI/UIGame';
|
||||
return 'res://UI/Game/UIGame';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取世界根节点
|
||||
*/
|
||||
public getWorldRoot(): Node {
|
||||
return this.worldRoot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { _decorator, EditBox, Button } from 'cc';
|
||||
import { UIBase } from '../../Framework/UI/UIBase';
|
||||
import { _decorator, Button, EditBox } from 'cc';
|
||||
import { NetManager } from '../../Framework/Net/NetManager';
|
||||
import { ReqLogin, ResLogin } from '../../Framework/Net/LoginProtocol';
|
||||
import { UIBase } from '../../Framework/UI/UIBase';
|
||||
import { AppStatusManager } from '../AppStatus/AppStatusManager';
|
||||
import { LoginMessagePair } from '../Msg/Pair/LoginMessagePair';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@@ -19,34 +19,34 @@ const { ccclass } = _decorator;
|
||||
export class UILogin extends UIBase {
|
||||
/** 账号输入框 */
|
||||
private _inputAccount: EditBox | null = null;
|
||||
|
||||
|
||||
/** 登录按钮 */
|
||||
private _btnLogin: Button | null = null;
|
||||
|
||||
|
||||
/** 是否正在登录中 */
|
||||
private _isLogging: boolean = false;
|
||||
|
||||
|
||||
/**
|
||||
* 获取UI资源路径
|
||||
*/
|
||||
onGetUrl(): string {
|
||||
return 'res://UI/Login/UILogin';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UI初始化
|
||||
*/
|
||||
async onStart(params?: any): Promise<void> {
|
||||
console.log('[UILogin] 登录界面初始化', params);
|
||||
|
||||
|
||||
// 使用GetChild查找子节点
|
||||
this._inputAccount = this.GetChild('mid/input_account', EditBox);
|
||||
this._btnLogin = this.GetChild('mid/btn_login', Button);
|
||||
|
||||
|
||||
// 使用SetClick绑定点击事件
|
||||
this.SetClick(this._btnLogin, this.onLoginClick, this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 登录按钮点击
|
||||
*/
|
||||
@@ -55,21 +55,21 @@ export class UILogin extends UIBase {
|
||||
console.log('[UILogin] 正在登录中,请稍候...');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 获取账号
|
||||
const account = this._inputAccount?.string?.trim() || '';
|
||||
|
||||
|
||||
if (!account) {
|
||||
console.warn('[UILogin] 请输入账号');
|
||||
// TODO: 显示提示UI
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
console.log(`[UILogin] 开始登录,账号: ${account}`);
|
||||
|
||||
|
||||
// 开始登录
|
||||
this._isLogging = true;
|
||||
|
||||
|
||||
try {
|
||||
await this.login(account);
|
||||
} catch (error) {
|
||||
@@ -79,29 +79,29 @@ export class UILogin extends UIBase {
|
||||
this._isLogging = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 执行登录
|
||||
* @param account 账号
|
||||
*/
|
||||
private async login(account: string): Promise<void> {
|
||||
const netManager = NetManager.getInstance();
|
||||
|
||||
|
||||
// 使用类型化的登录协议
|
||||
const result = await netManager.callApi<ReqLogin, ResLogin>('Login', {
|
||||
account: account
|
||||
});
|
||||
|
||||
const result = await netManager.callMsg(new LoginMessagePair(), {
|
||||
playerId: account
|
||||
},);
|
||||
|
||||
if (result && result.success) {
|
||||
console.log('[UILogin] 登录成功:', result);
|
||||
|
||||
|
||||
// 登录成功,切换到游戏状态
|
||||
const appStatusManager = AppStatusManager.getInstance();
|
||||
appStatusManager.changeState('Game', {
|
||||
player: result.player,
|
||||
isNewPlayer: result.isNewPlayer || false
|
||||
});
|
||||
|
||||
|
||||
// 隐藏登录界面
|
||||
this.hide();
|
||||
} else {
|
||||
@@ -109,7 +109,7 @@ export class UILogin extends UIBase {
|
||||
// TODO: 显示错误提示UI
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UI清理
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
/* Base configuration. Do not edit this field. */
|
||||
"extends": "./temp/tsconfig.cocos.json",
|
||||
|
||||
/* Add your custom configuration here. */
|
||||
"compilerOptions": {
|
||||
"strict": false,
|
||||
"target": "es2020",
|
||||
"downlevelIteration": true
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user