本次尝试,模拟了一个小人物在场景中行走,使用简单模型建立了森林,图片纹理模拟草地,加载3D模型呈现人物,使用按键asdw模拟人物的行走,行走和站立时人物的切换等。

主要用到点:3D模型的加载,模型的动画(行走与站立)之间的切换。

不足之处:没有检测碰撞与边界。

需要注意的是,代码需要运行在服务器端,切记。

代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
</style>
<title>测试</title>
<script src="../js/three.js"></script>
<script src="../js/dat.gui.min.js"></script>
<script src="../js/OrbitControls.js"></script>
<script src="../js/util.js"></script>
<script src="../js/GLTFLoader.js"></script>
</head>
<body>
<script>
var scene, camera, renderer, controls, flag = 1;
var target;
var trees; // 荧光棒
var role; // 主角
var textPlane;
var stateList = {};
var actionMap = {
up: { direction: 'up', rotation: Math.PI, axes: 'z' },
down: { direction: 'down', rotation: 0, axes: 'z' },
left: { direction: 'left', rotation: - Math.PI / 2, axes: 'x' },
right: { direction: 'right', rotation: Math.PI / 2, axes: 'x' }
};
var nopeAction = { direction: null };
var nextAction = { direction: 'down', rotation: 0 };
var clock, mixer, currentAction, previousAction, lastkey;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 500);
camera.position.z = 20;
camera.lookAt(scene.position);
scene.add(camera);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
document.body.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.update();
clock = new THREE.Clock();
window.addEventListener('resize', onWindowResize, false);
var axesHelper = new THREE.AxesHelper(50);
scene.add(axesHelper);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
}
function run() {
init();
createRole(); //创建角色
createAmbientLight(); // 绘制环境光
// createSpotlist(new THREE.Vector3(50, 50, 50), role);
createPlane(); // 创建舞台平面
createTrees(20);
window.addEventListener('keydown', keyPressed, false);
window.addEventListener('keyup', keyUp, false)
render();
}
function render() {
var dt = clock.getDelta();
if (mixer) mixer.update(dt);
requestAnimationFrame(render);
controls.update();
handleRoleAction();
renderer.render(scene, camera);
}
run();
function createAmbientLight() {
var light = new THREE.AmbientLight(0xdddddd); // soft white light
scene.add(light);
}
function createPlane() {
//Create a plane that receives shadows (but does not cast them)
var planeGeometry = new THREE.PlaneBufferGeometry(1000, 1000);
var texture = new THREE.TextureLoader().load('../images/grasslight-big.jpg');
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(25, 25);
var planeMaterial = new THREE.MeshStandardMaterial({ map: texture, side: THREE.DoubleSide });
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -16;
plane.receiveShadow = true;
scene.add(plane);
}
function createSpotlist(Vector3, target) {
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(Vector3.x, Vector3.y, Vector3.z);
spotLight.castShadow = true;
spotLight.angle = Math.PI / 18;
spotLight.shadow.mapSize.width = 512;
spotLight.shadow.mapSize.height = 512;
spotLight.shadow.camera.near = 0.5;
spotLight.shadow.camera.far = 500;
spotLight.shadow.camera.fov = 30;
spotLight.target = target;
scene.add(spotLight);
// Create a helper for the spotlight
// var helper = new THREE.SpotLightHelper(spotLight);
// scene.add(helper);
// //Create a helper for the shadow camera (optional)
// var helper = new THREE.CameraHelper(spotLight.shadow.camera);
// scene.add(helper);
}
function createTrees(num) {
for (let i = 0; i < num; i++) {
let treeNode = new THREE.Object3D();
var treeTopGeo = new THREE.CylinderGeometry(0, 10, 20, 32);
var treeTopMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
var treeTop = new THREE.Mesh(treeTopGeo, treeTopMaterial);
treeTop.position.y = 4; // 树底部中心点高度是-11,底部的上边高度是-6,这样树顶部中心点高度默认是0的话,下边是-10,如果想让下边高度为-6,则中心点高度为4
var treeBottomGeo = new THREE.CylinderGeometry(5, 5, 10, 32);
var treeBottomMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
var treeBottom = new THREE.Mesh(treeBottomGeo, treeBottomMaterial);
treeBottom.position.y = -11; // 底面位置是-16,底部圆柱体中心点高度默认是0的话,底边高度是-5,所以将position.y设置为-11,这样下边高度是-16
treeNode.add(treeTop);
treeNode.add(treeBottom);
treeNode.position.set(util.createRandomPos(-250, 250), 0, util.createRandomPos(-250, 250));
scene.add(treeNode);
}
}
function createRole() {
// model
var loader = new THREE.GLTFLoader();
loader.load('../models/RobotExpressive.glb', function (gltf) {
role = gltf.scene;
role.position.y = -16;
mixer = new THREE.AnimationMixer(role);
stateList.Walking = mixer.clipAction(gltf.animations[10]);
stateList.Standing = mixer.clipAction(gltf.animations[8]);
// 设置下面两项主要是站着的时候,别抖了
stateList.Standing.clampWhenFinished = true;
stateList.Standing.loop = THREE.LoopOnce;
currentAction = stateList.Standing;
currentAction.play();
scene.add(role);
createSpotlist(new THREE.Vector3(50, 50, 50), role);
}, undefined, function (e) {
console.error(e);
});
}
function keyPressed(e) {
var key = event.keyCode;
if (lastkey != key) {
lastkey = key;
fadeToAction('Walking', 0.2);
}
switch (key) {
case 87:
/*w*/
nextAction = actionMap.up;
break;
case 65:
/*a*/
nextAction = actionMap.left;
break;
case 83:
/*s*/
nextAction = actionMap.down;
break;
case 68:
/*d*/
nextAction = actionMap.right;
break;
}
if (role) role.rotation.y = nextAction.rotation;
}
function keyUp() {
lastkey = null;
nextAction = nopeAction;
fadeToAction('Standing', 0.2);
}
function handleRoleAction() {
if (role) {
if (nextAction.direction == 'down' || nextAction.direction == "right") {
flag = 1;
} else if (nextAction.direction == 'up' || nextAction.direction == "left") {
flag = -1;
}
else {
flag = 0;
}
role.position[nextAction.axes] += 0.2 * flag;
}
}
function fadeToAction(name, duration) {
previousAction = currentAction;
currentAction = stateList[name];
if (previousAction !== currentAction) {
previousAction.fadeOut(duration);
}
if (currentAction) {
currentAction
.reset()
.setEffectiveTimeScale(1)
.setEffectiveWeight(1)
.fadeIn(duration)
.play();
}
}
</script>
</body>
</html>
附上github链接:https://github.com/liujiekun/threeJS记得在服务端启动,直接浏览器运行是看不到图片、纹理以及动画原型的。

three.js尝试(二)模拟游戏开发:3D人物在地图上行走的更多相关文章

  1. 喵的Unity游戏开发之路 - 在球体上行走

    很多童鞋没有系统的Unity3D游戏开发基础,也不知道从何开始学.为此我们精选了一套国外优秀的Unity3D游戏开发教程,翻译整理后放送给大家,教您从零开始一步一步掌握Unity3D游戏开发. 本文不 ...

  2. 拜托,使用Three.js让二维图片具有3D效果超酷的好吗 💥

    声明:本文涉及图文和模型素材仅用于个人学习.研究和欣赏,请勿二次修改.非法传播.转载.出版.商用.及进行其他获利行为. 背景 逛 sketchfab 网站的时候我看到有很多二维平面转 3D 的模型例子 ...

  3. [Unity3D]Unity3D游戏开发3D选择场景中的对象,并显示轮廓效果强化版

    大家好,我是秦培,欢迎关注我的博客,我的博客地址blog.csdn.net/qinyuanpei. 在上一篇文章中,我们通过自己定义着色器实现了一个简单的在3D游戏中选取.显示物体轮廓的实例. 在文章 ...

  4. 【游戏开发】小白学Lua(上)

    在很多游戏中,脚本语言是不可或缺的一部分,很多游戏都使用到了Lua,js,python一类的脚本,脚本语言可以在很多方面给开发进程带来帮助.脚本语言可以作为初始化文件读入变量和游戏数据的一个快速而方便 ...

  5. html5游戏开发--"动静"结合用地图块拼成大地图 & 初探lufyl

    一.前言   本次教程将向大家讲解如何用html5将小地图块拼成大地图,以及如何用现有的高级html5游戏开发库件lufylegend.js开发游戏.   首先让我们来了解了解如何用html5实现动画 ...

  6. Unity游戏开发--30s制作精美地图

    "君子生非异也.善假于物也"--<劝学>荀子 引用这句话的目的,是我觉得有时候.利用工具来提高游戏开发效率是很必要的. 利用工具,解放程序员双手. 今天想给大家介绍下. ...

  7. three.js尝试(一)模拟演唱会效果

    工作闲暇之余,偶然翻到了Three.js的官网,立刻被它酷炫的案例给惊艳到了,当即下定决心要试验摸索一番,于是看demo,尝试,踩坑,解决问题,终于搞定了,一个模拟演唱会场景. 主角围绕一个钢管在舞动 ...

  8. 游戏开发3D基础知识

    概念学习: 向量 向量简介 我们将所有彼此平行的向量进行平移,使其起点与坐标原点重合,当某一向量的起始端与坐标原点重合,我们成该向量处于标准位置.这样,我们就可用向量的终点坐标来描述一个处于标准位置的 ...

  9. 从一点儿不会开始——Unity3D游戏开发学习(一)

    一些废话 我是一个windows phone.windows 8的忠实粉丝,也是一个开发者,开发数个windows phone应用和两个windows 8应用.对开发游戏一直抱有强烈兴趣和愿望,但奈何 ...

随机推荐

  1. 「查缺补漏」巩固你的Redis知识体系

    Windows Redis 安装 链接: https://pan.baidu.com/s/1MJnzX_qRuNXJI09euzkPGA 提取码: 2c6w 复制这段内容后打开百度网盘手机App,操作 ...

  2. sql server 存储过程的(包含事务)方法里面,采用游标循环,批量删除(修改)数据

    sqlserver 数据库 1.下面是完整的 在存储过程中 使用游标进行 循环删除的实例(包括存储过程中,事务的应用) 2.有问题的话,欢迎随时讨饶我,相信大家看下注释应该就能明白了,很简单的一个,小 ...

  3. 微信公众号添加word文件

    微信公众号添加word文件的教程 我们都知道创建一个微信公众号,在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序“微附件”进行在公众号中添加附件. 以下是公众 ...

  4. 基于OpenSIPS做注册服务下,场景A打B,一方发起BYE挂断后收到500,另一方无法挂断的问题

    基于OpenSIPS做注册服务下,场景A打B,一方发起BYE挂断后收到500,另一方无法挂断的问题     最近在工作中遇到一个看似很奇怪的,排除起来很费劲,但最后的解决方式又及其简单的问题,下面我们 ...

  5. Socket 模拟HTTP客户端请求

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import ja ...

  6. React的useEffect与useLayoutEffect执行机制剖析

    引言 useEffect和useLayoutEffect是React官方推出的两个hooks,都是用来执行副作用的钩子函数,名字类似,功能相近,唯一不同的就是执行的时机有差异,今天这篇文章主要是从这两 ...

  7. C#LeetCode刷题之#225-用队列实现栈(Implement Stack using Queues)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4106 访问. 使用队列实现栈的下列操作: push(x) -- ...

  8. C#设计模式之6-适配器模式

    适配器模式(Adapter Pattern) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/399 访问. 适配器模式属 ...

  9. Revit二次开发——非模态窗口的事件处理

    一.起因    自己在写revit二开时,有一个Winform窗体按钮点击事件需要 触发调用事务进行处理,结果出现“异常“Starting a transaction from an external ...

  10. 记一次生产环境tomcat线程数打满情况分析

    前言 旨在分享工作中遇到的各种问题及解决思路与方案,与大家一起学习. -- 学无止境, 加油 ! Just do it ! 问题描述 运行环境描述 tomcat-8.5 单节点(该应用集群20个节点) ...