three.js 按键W前进、S退后、A左转、D右转运动

效果:W 键 前进;S 键后退;A 键左转;D 键右转;使用了 tween.js 动画库; 

代码:

<template>
  <div>
    <el-container>
      <el-main>
        <div class="box-card-left">
          <div id="threejs"></div>
        </div>
      </el-main>
    </el-container>
  </div>
</template>s
<script>
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import TWEEN from '@tweenjs/tween.js';
export default {
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null,
      res1: null,
      res2: null,
      clock: null,
      keyState: {
        W: false,
        S: false,
        A: false,
        D: false,
      },
      left_rotation: true, // 向左旋转的标志
      right_rotation: true, // 向右旋转的标志
      VW: new this.$three.Vector3(0, 0, 0),
      VS: new this.$three.Vector3(0, 0, 0),
      curr_v: new this.$three.Vector3(0, 0, 0),
      person: null,
      deltaTime: 0,
      a: 30, // 加速度
      damping: -0.04,
    };
  },
  created() {},
  mounted() {
    this.name = this.$route.query.name;
    this.init();
  },
  methods: {
    goBack() {
      this.$router.go(-1);
    },
    init() {
      this.clock = new this.$three.Clock();
      // 创建场景
      this.scene = new this.$three.Scene();
      // 创建辅助坐标轴对象
      const axesHelper = new this.$three.AxesHelper(100);
      this.scene.add(axesHelper);
      // 创建环境光

      const ambientLight = new this.$three.AmbientLight(0xffffff, 10);
      this.scene.add(ambientLight);
      // 创建相机对象
      this.camera = new this.$three.PerspectiveCamera(60,1,0.01,2000);
      this.camera.position.set(10,10,10);
      this.camera.lookAt(0,0,0);
      // 创建渲染器对象
      this.renderer = new this.$three.WebGLRenderer();
      this.renderer.setSize(1500,1200);
      // 创建GLTFLoader对象;加载人物模型
      const gltfLoader = new GLTFLoader();
      gltfLoader.load("models/gltf/person2/scene.gltf", gltf => {
        gltf.scene.position.set(0,0,-10);
        gltf.scene.scale.set(2,2,2);
        this.person = gltf.scene;
        this.scene.add(gltf.scene);
        this.renderer.render(this.scene, this.camera);
        window.document.getElementById("threejs").appendChild(this.renderer.domElement);
      })
      const controls = new OrbitControls(this.camera, this.renderer.domElement);
      controls.addEventListener("change", () => {
        this.renderer.render(this.scene, this.camera);
      })
      this.addEventListenerFn();
      this.renderLoop();
      
    },
    renderLoop() {
      const deltaTime = this.clock.getDelta();
      if(this.keyState.W) {
        if(this.VW.length() < 5) {
          this.VW.add(new this.$three.Vector3(0,0,1).multiplyScalar(this.a * deltaTime));
          this.curr_v = this.VW.clone();
        }
        let pos = this.VW.clone().multiplyScalar(deltaTime);
        this.person.position.add(pos);
      }

      if(this.keyState.S) {
        if(this.VS.length() < 5) {
          this.VS.add(new this.$three.Vector3(0,0,-1).multiplyScalar(this.a * deltaTime));
          this.curr_v = this.VS.clone();
        }
        let pos = this.VS.clone().multiplyScalar(deltaTime);
        this.person.position.add(pos);
      }
      if(this.keyState.A) {
      }
      if(this.person) {
        // .addScaledVector ( v : Vector3, s : Float ) : 将所传入的v与s相乘所得的乘积和这个向量相加。
        this.curr_v.addScaledVector(this.curr_v, this.damping);
        let pos = this.curr_v.clone().multiplyScalar(deltaTime);
        this.person.position.add(pos);
      }
      this.renderer.render(this.scene, this.camera);
      
      TWEEN.update();
      requestAnimationFrame(this.renderLoop);
    },
    addEventListenerFn() {
      // 监听按下的 W 键
      document.addEventListener("keydown", e => {
        if(e.code == "KeyW") {
          this.keyState.W = true;
        }
        if(e.code == "KeyS") {
          this.keyState.S = true;
        }
        if(e.code == "KeyA") {
          this.keyState.A = true;
          if(!this.left_rotation)return false;
          const tween0 = new TWEEN.Tween(this.person.rotation);
          let deg = this.$three.MathUtils.radToDeg(this.person.rotation.y);
          let rad = this.$three.MathUtils.degToRad(deg + 90);
          
          if(rad != null) {
            tween0.to({x:0, y: rad, z:0}, 1000);
            tween0.start();
            tween0.onStart(() => {
              this.left_rotation = false;
            });
            tween0.onComplete(() => {
              this.left_rotation = true;
            });
          }
        }
        if(e.code == "KeyD") {
          this.keyState.D = true;
          if(!this.right_rotation)return false;
          const tween0 = new TWEEN.Tween(this.person.rotation);
          let deg = this.$three.MathUtils.radToDeg(this.person.rotation.y);
          let rad = this.$three.MathUtils.degToRad(deg - 90);
          
          if(rad != null) {
            tween0.to({x:0, y: rad, z:0}, 1000);
            tween0.start();
            tween0.onStart(() => {
              this.right_rotation = false;
            });
            tween0.onComplete(() => {
              this.right_rotation = true;
            });
          }
        }
      })
      document.addEventListener("keyup", e => {
        if(e.code == "KeyW") {
          this.keyState.W = false;
          this.VW = new this.$three.Vector3(0, 0, 0);
        }
        if(e.code == "KeyS") {
          this.keyState.S = false;
          this.VS = new this.$three.Vector3(0, 0, 0);
        }
        if(e.code == "KeyA") {
          this.keyState.A = false;
        }
        if(e.code == "KeyD") {
          this.keyState.D = false;
        }
      })
    }
  },
};
</script>
<style lang="less" scoped>
.box-card-left {
  display: flex;
  align-items: flex-start;
  flex-direction: row;
  width: 100%;
  .box-right {
    img {
      width: 500px;
      user-select: none;
    }
  }
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/450973.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ctfshow-XXE(web373-web378)

目录 XXE&#xff08;外部实体注入攻击&#xff09; web373 web374 web375 web376 web377 web378 知识点 XXE&#xff08;外部实体注入攻击&#xff09; XXE这几关有个前提flag在根目录下文件名为flag web373 <?php error_reporting(0); libxml_disable_entity_…

商铺办理房产证需缴纳的交易费用有哪些材料?

一、商铺办理房产证收费标准 一般来说&#xff0c;商铺办理房产证时需要缴纳的交易费用包括&#xff1a;交易费、登记费、验资费、营业税、个人所得税、契税等。 每个城市的缴费标准会有所不同。 具体缴纳费用需要咨询当地房管局。 1、契税&#xff1a;房屋交易额的5%。 &…

BUUCTF-----[SWPU2019]Web1

打开页面&#xff0c;原本以为是二次注入,结果不是&#xff0c;先注册一个账户 在申请发布广告中&#xff0c;发现反射性xss(然而没有什么用) 在广告申请名字中发现注入点 开始注入 通过一系列的测试&#xff0c;发现系统过滤了#&#xff0c;or&#xff0c;空格 orde…

C语言 - 各种自定义数据类型

1.结构体 把不同类型的数据组合成一个整体 所占内存长度是各成员所占内存的总和 typedef struct XXX { int a; char b; }txxx; txxx data; typedef struct XXX { int a:1; int b:1; …

如果电脑缺少dll文件怎么解决?如何快速解决dll丢失问题

最近有小伙伴问电脑老是缺少dll文件&#xff0c;这种问题到底要怎么去解决呢&#xff1f;其实这种现象是正常的&#xff0c;为啥说正常呢&#xff0c;下面我们会给大家详细的讲解dll为啥会缺少&#xff0c;然后还会讲解电脑缺少dll文件怎么解决的方法&#xff0c;好了&#xff…

Linux: 预备

计算机结构基础 由于速度原因, CPU不直接与外设打交道, 而是通过内存进行交互.(CPU速度 >> 外设) 操作系统: 内核 (管理软硬件) shell(给用户使用操作系统的方式) 操作系统封装了用户操作接口 相比于系统接口,使用更简单跨平台性: 不同的操作系统,其系统调用接口是不同…

代码随想录 贪心算法-难度题目-其他题目

目录 53.最大子数组和 134.加油站 968.监控二叉树 53.最大子数组和 53. 最大子数组和 中等 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个…

(SUB)app性能测试

APP性能测试(启动速度、内存、CPU、FPS、GPU、耗电量)_性能测试中fps分析-CSDN博客 app性能测试_app性能测试方案-CSDN博客 测试方案 服务端 平均响应时间 错误率 吞吐量 CPU/内存占用率 网络/硬盘的读写速度 客户端 启动速度 手机APP的启动时长是一个很容易被用户感…

【数据库】Oracle内存结构与参数调优

Oracle内存结构与参数调优 Oracle 内存结构概览oracle参数配置概览重要参数&#xff08;系统运行前配置&#xff09;:次要参数&#xff08;可在系统运行后再优化调整&#xff09;: Oracle数据库服务器参数如何调整OLTP内存分配操作系统核心参数配置Disabling ASMM&#xff08;禁…

JSON基础知识

目录 一、定义二、作用三、特点四、语法JSON具有以下这些形式&#xff1a;4.1 对象(JSONObject)&#xff1a;4.2 数组(JSONArray)&#xff1a;4.3 值4.4 字符串4.5 数值 五、常用的JSON解析方式5.1 org.json解析5.1.1 常用api5.1.2 get方法与opt方法对比5.1.3 使用示例5.1.3 参…

面试不再愁,看这份保姆级简历写作指南

在现代社会&#xff0c;简历是求职过程中至关重要的一环。一份精心设计的简历可以为你打开求职的大门&#xff0c;让你脱颖而出。然而&#xff0c;许多求职者常常陷入简历写作的困境&#xff0c;不知道从何处入手。在这篇文章中&#xff0c;我将手把手地教你如何写一份引人注目…

Jmeter接口自动化-如何解决请求头Content-Type冲突问题

一、前言 通常我们在使用Jmeter做接口自动化时&#xff0c;在线程组里添加HTTP信息头管理器&#xff0c;用来管理公共的请求头信息。普通的接口自动化是没问题的&#xff0c;但是对于有些特殊的操作流程&#xff0c;如&#xff1a;先上传文件接口&#xff08;信息头使用Conten…

openGauss学习笔记-240 openGauss性能调优-SQL调优-更新统计信息

文章目录 openGauss学习笔记-240 openGauss性能调优-SQL调优-更新统计信息240.1 背景信息240.2 操作步骤 openGauss学习笔记-240 openGauss性能调优-SQL调优-更新统计信息 在数据库中&#xff0c;统计信息是规划器生成计划的源数据。没有收集统计信息或者统计信息陈旧往往会造…

01 THU大模型之基础入门

1. NLP Basics Distributed Word Representation词表示 Word representation: a process that transform the symbols to the machine understandable meanings 1.1 How to represent the meaning so that the machine can understand Compute word similarity 计算词相似度 …

Huggingface中Transformer模型使用

一、Huggingface介绍 1、Huggingface定位 NLP自从Transformer模型出现后&#xff0c;处理方式有大统一的趋势&#xff0c;首先回答几个基础问题&#xff1a; 1、自然语言处理究竟要做一件什么事呢&#xff1f;自然语言处理最终解决的是分类问题&#xff0c;但是它不仅仅输出…

算法之二分查找算法

二分查找算法简介 1. 首先说明二分查找算法是比较恶心, 细节很多, 很容易写出死循环的算法, 但熟悉了之后是最简单的算法. 2. 其次我们可能听说过二分查找的前提是数组有序的前提下进行, 但其实不一定. 3. 二分查找算法有一套模板: 朴素的二分模板: 比较简单, 但是有局限性查找…

运维自动化之ansible

pxe 一键安装操作系统 操作系统只是提供一个平台 lnmp 需要多软件协同完成的一个简单项目 服务器正常运行 日常运维 巡检 服务器上的软件正常运行 zabbix 普罗米修斯 系统调优&#xff0c;架构调优 云计算核心职能 搭建平台架构 日常运营保障 性能效率优化 相关工具 代…

SDWAN专线对企业接入有门槛吗

SD-WAN&#xff08;软件定义广域网&#xff09;技术作为一种新型的网络解决方案&#xff0c;正在成为企业网络接入的热门选择。然而&#xff0c;对于企业来说&#xff0c;接入SD-WAN专线是否存在门槛&#xff0c;是一个值得探讨的问题。本文将从不同角度分析SD-WAN专线对企业接…

HTML 学习笔记(十一)表单

一、分块 1.单行文本框控件–文本框和密码框 文本框控件通过单标签input实现&#xff0c;其具有必要属性type来控制输入控件的类型(默认为text即文本信息)&#xff0c;密码框的type为password(口令)。   表单的动作属性定义了目的文件的文件名。由动作属性定义的这个文件通常…

国内可用免费AI工具集

1、Kimi Chat 由月之暗面科技有限公司&#xff08;Moonshot AI&#xff09;开发的人工智能助手。擅长中英文对话&#xff0c;能够提供安全、有帮助且准确的回答。它的能力包括阅读和理解用户上传的文件&#xff0c;访问互联网内容&#xff0c;以及结合搜索结果来回答问题。比如…