• three.js尝试(二)模拟游戏开发:3D人物在地图上行走


    本次尝试,模拟了一个小人物在场景中行走,使用简单模型建立了森林,图片纹理模拟草地,加载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记得在服务端启动,直接浏览器运行是看不到图片、纹理以及动画原型的。
  • 相关阅读:
    Codeforces Round #547 F1&F2. Same Sum Blocks(贪心)
    Codeforces Round #547 D. Colored Boots(贪心)
    Codeforces Round #547 C. Polycarp Restores Permutation(二分枚举/数学+模拟)
    CCF 201812-4 数据中心(最小生成树)
    CCF【小明放学&小明上学】
    TIME_WAIT状态
    ping的详细过程
    两段不相邻子段和之和最大
    神水一题之“Who's in the Middle”
    日进一步之“A Knight's Journey”
  • 原文地址:https://www.cnblogs.com/liujiekun/p/10736599.html
Copyright © 2020-2023  润新知