> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/playcanvas-plugin/first-person-view.md).

# 第一人称视角

第一人称视角——Convai 集成的 PlayCanvas 插件指南。

### 向平面添加物理组件

1. 放大平面并为其添加物理组件。添加碰撞和刚体。
2. 导入 Ammo.js（启用物理）。根据平面大小放大 Collision 组件。

<figure><img src="https://2402152281-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtUJA212Zc1S9ACc8T4l%2Fuploads%2FGjJz0gy52pnJ5CKzw2ym%2FScreenshot%20(21).png?alt=media&amp;token=f36b7f54-3dd7-46ca-a93d-11f5f06556cb" alt=""><figcaption><p>平面上的物理组件</p></figcaption></figure>

### 玩家主体

1. **创建新实体**
   * 在 PlayCanvas 编辑器中，在 Hierarchy 面板里右键单击并选择“创建新实体”。
2. **添加物理组件**
   * 选中新实体后，点击编辑器右上角的“添加组件”按钮。
   * 搜索“物理”并将“物理”组件添加到实体。
3. **添加碰撞胶囊组件**
   * 在实体仍然被选中的情况下，再次点击“添加组件”。
   * 搜索“碰撞”并将“碰撞胶囊”组件添加到实体。
4. **调整实体 Y 位置**
   * 调整 Translation 的“Y”值，将实体定位到平面上方。
5. **添加刚体组件**
   * 在实体仍然被选中的情况下，再次点击“添加组件”。
   * 搜索“刚体”并将“刚体”组件添加到实体。
   * 将刚体的“类型”设置为“动态”。
6. **调整角因子**
   * 在刚体组件中，找到“角因子”部分。
   * 将角因子的“X”、“Y”和“Z”值设为 0。

<figure><img src="https://2402152281-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtUJA212Zc1S9ACc8T4l%2Fuploads%2FiDVjhJv3eFTNanhYVO9W%2FScreenshot%20(22).png?alt=media&amp;token=c91c5599-bf59-43c5-b0e0-7f56ddd02589" alt=""><figcaption><p>玩家组件的物理组件</p></figcaption></figure>

### 第一人称视角摄像机控制

创建一个 FirstPersonView\.js 脚本并添加下面的代码。你可以在以下位置找到实现摄像机控制的示例 [PlayCanvas 文档](https://api.playcanvas.com/) 以及示例。将此脚本附加到 Player 胶囊体。

```javascript
var FirstPersonMovement = pc.createScript('firstPersonMovement');
var fpsCamera = null
var overChat = false
FirstPersonMovement.attributes.add('power', {
    type: 'number',
    default: 2500,
    description: '调整玩家移动速度'
});

FirstPersonMovement.attributes.add('lookSpeed', {
    type: 'number',
    default: 0.07,
    description: '调整观察灵敏度'
});
// 每个实体只调用一次的初始化代码
FirstPersonMovement.prototype.initialize = function() {
    this.force = new pc.Vec3();
    this.eulers = new pc.Vec3();

    var app = this.app;
    app.mouse.on("mousemove", this._onMouseMove, this);
    app.mouse.on("mousedown", function () { 
        if(!overChat){
            app.mouse.enablePointerLock();
        }   
    },this);
    // 检查所需组件
    if (!this.entity.collision) {
        console.error("First Person Movement 脚本需要具有 'collision' 组件");
    }

    if (!this.entity.rigidbody || this.entity.rigidbody.type !== pc.BODYTYPE_DYNAMIC) {
        console.error("First Person Movement 脚本需要具有 DYNAMIC 'rigidbody' 组件");
    }
};

// 每帧调用的更新代码
FirstPersonMovement.prototype.update = function(dt) {
// 如果未在编辑器中分配摄像机，则创建一个
    if (!this.camera) {
        this._createCamera();
    }

    var force = this.force;
    var app = this.app;

    fpsCamera = this.camera;
    var forward = this.camera.forward;
    var right = this.camera.right;

    // 移动 
    var x = 0;
    var z = 0;

    const convaiFormEl = document.getElementById("convai-input");
    // 使用 W-A-S-D
    if(app.keyboard.isPressed(pc.KEY_A) && !(document.activeElement === convaiFormEl)){
         x -= right.x;
         z -= right.z;
    }

      if (app.keyboard.isPressed(pc.KEY_D) && !(document.activeElement === convaiFormEl)) {
        x += right.x;
        z += right.z;
    }

    if (app.keyboard.isPressed(pc.KEY_W) && !(document.activeElement === convaiFormEl)) {
        x += forward.x;
        z += forward.z;
    }

    if (app.keyboard.isPressed(pc.KEY_S) && !(document.activeElement === convaiFormEl)) {
        x -= forward.x;
        z -= forward.z;
    }

    // 使用按键方向对角色施加力
    if (x !== 0 || z !== 0) {
        force.set(x, 0, z).normalize().scale(this.power);
        this.entity.rigidbody.applyForce(force);
    }

    // 根据鼠标事件更新摄像机角度
    this.camera.setLocalEulerAngles(this.eulers.y, this.eulers.x, 0);
};

FirstPersonMovement.prototype._onMouseMove = function (e) {
    // 如果指针被禁用
    // 如果按下左键，则根据鼠标移动更新摄像机
    if (pc.Mouse.isPointerLocked() || e.buttons[0]) {
        this.eulers.x -= this.lookSpeed * e.dx;
        this.eulers.y -= this.lookSpeed * e.dy;
    }

    const convaiChat = document.getElementById("convai-chat")
    convaiChat.addEventListener("mouseover",()=>{
    overChat = true;
    })

    convaiChat.addEventListener("mouseout",()=>{
    overChat = false;
    })
};

FirstPersonMovement.prototype._createCamera = function () {
    // 如果用户尚未分配摄像机，则创建一个新的
    this.camera = new pc.Entity();
    this.camera.setName("第一人称摄像机");
    this.camera.addComponent("camera");
    this.entity.addChild(this.camera);
    this.camera.translateLocal(0, 0.5, 0);
};
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/playcanvas-plugin/first-person-view.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
