본문 바로가기
자바스크립트와 캔버스

바빌론JS - 중력과 컬리젼

바빌론JS의 컬리젼(Collision)에 대해 알아보았는데요.
바빌론JS에서 중력과 같은 것을 가능하게 하는 것입니다.

근데 이 컬리젼을 적용하려면 그동안 작업했던 것들을 포기하고
거의 새로 만들어야 하다시피 하지 않겠습니까?

하지만 그만큼 매력적이기도 하기에 케이군과 함께 새로 시작해보았습니다 :)

지난 게시글 : https://itadventure.tistory.com/703

 

바빌론JS - 모바일웹에서 캐릭터 이동

지난 시간 비를 피하던 케이군이 드디어 집을 얻었습니다.https://itadventure.tistory.com/701 바빌론JS - 비를 피하고 싶어!지난 시간에는 케이군의 진짜 정체를 알아보았습니다.https://itadventure.tistory.com/

itadventure.tistory.com


CDN 파일 준비

바빌론JS에서는 CDN 다운로드를 제공하고 있는데요.
이 CDN 다운로드를 사용하면 바빌론 라이브러리를 내 사이트에서 제공할 수 있습니다.
실제 바빌론에서도 그걸 권장하고 있구요.
아래 게시글을 참조하여 CDN 파일을 내 PC 또는 내 서버에 올려놓으세요.

https://itadventure.tistory.com/707

 

바빌론JS - CDN 파일을 내 서버에

바빌론JS 에서는 프로덕트 ( 상용서비스) 환경에서 자기네 CDN 서버를 사용하지 말라고 하는데요.그 이유는 파일이 갑자기 변경될 수도 있고 CDN 서버 요금 발생 때문도 아닐까 생각됩니다 :)꼭 그

itadventure.tistory.com

snapshot 폴더의 압축을 풀어 아래와 같이 위치하면 되는데요.
( 꼭 /var/www/html/babylon/snapshot 폴더가 아니어도 됩니다. )

앞으로의 예제는 snapshot 상위 폴더에서 ex21. ex22 등의 폴더를 생성해서 진행할 겁니다.

ex21 폴더에서 다운받은 snapshot 경로를 참조하려면,
index.html 파일을 아래와 같이 구성하면 되는데요.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Babylon.js App</title>

    <style>
        html, body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>

</head>
<body>

    <canvas id="renderCanvas"></canvas>
    <script src="../snapshot/babylon.js"></script>
    <script src="../snapshot/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
    <script src="../snapshot/gui/babylon.gui.min.js"></script>
     <script src="../snapshot/serializers/babylonjs.serializers.js"></script>
    <script src="../snapshot/earcut.min.js"></script>    
    <script src="./main.js"></script>
</body>
</html>

그 외 다른건 기존 index.html 과 동일한 규칙입니다.
이어서 main.js 파일을 아래 내용으로 저장합니다.

const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas,true);
// 입력 시스템
const deviceInputSystem = new BABYLON.DeviceSourceManager(engine);
// 마우스
const mouseDeviceSource = deviceInputSystem.getDeviceSource(BABYLON.DeviceType.Mouse);

let mesh_npc = null;
let currentCamera = null;
let arc_camera;
let inputMap = {};
let walk_anim = null;

var createScene = function () {
    var scene = new BABYLON.Scene(engine);

    scene.gravity = new BABYLON.Vector3(0, -0.9, 0);
    scene.collisionsEnabled = true;

    // 키보드 이벤트     
    scene.actionManager = new BABYLON.ActionManager(scene);
    scene.actionManager.registerAction(
        new BABYLON.ExecuteCodeAction(
            BABYLON.ActionManager.OnKeyDownTrigger, 
            function (evt) {
                inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
    }));
    scene.actionManager.registerAction(
        new BABYLON.ExecuteCodeAction(
            BABYLON.ActionManager.OnKeyUpTrigger, 
            function (evt) {
                inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
    }));

    arc_camera = new BABYLON.ArcRotateCamera(
        "ArcCam", 
        BABYLON.Tools.ToRadians(90), 
        BABYLON.Tools.ToRadians(65), 
        2,  // 카메라 거리
        BABYLON.Vector3.Zero(), 
        scene
    );

    ArcCam(scene, null);

    // 빛
    var light = new BABYLON.HemisphericLight(
        "light", 
        new BABYLON.Vector3(0, 1, 0), 
        scene
    );
    // 빛강도
    light.intensity = 1.5;

    var ground = BABYLON.MeshBuilder.CreateGround('ground', {width: 20, height: 20});
    ground.checkCollisions = true;
    var material = new BABYLON.StandardMaterial(scene);
    material.diffuseColor = new BABYLON.Color3(1.0, 0.2, 0.7);
    ground.material = material;

    var box = BABYLON.MeshBuilder.CreateBox('box', {height: 2, width: 2, depth: 2});
    box.checkCollisions = true;
    box.position.y = 1;
    var material2 = new BABYLON.StandardMaterial(scene);
    material2.diffuseColor = new BABYLON.Color3(0.2, 1.0, 0.7);
    box.material = material2;

    BABYLON.SceneLoader.ImportMeshAsync("", "./", "character01.glb")
    .then((result) => npc_callback(scene, result) );

    // 씬 리턴
    return scene;
};

function npc_callback(scene, result){
    var mesh = result.meshes[0];
    mesh_npc = mesh;
    mesh.scaling = new BABYLON.Vector3(0.08, 0.08, 0.08);
    mesh.position.y = 0.55;
    mesh.position.y = 1.55;

    ArcCam(scene, mesh_npc);
    
    // 컬리전
    mesh.checkCollisions = true;
    mesh.ellipsoid = new BABYLON.Vector3(0.15, 0.5, 0.15); // 컬리전 크기
    
    let step = 0.005;
    let p = 0;

    // 랜더 프레임별 실행 코드
    scene.onBeforeRenderObservable.add(() => {        
        walk_anim = scene.getAnimationGroupByName("walk");
        let forward_speed = 0.02;
        let backward_speed = -0.01;
        let turnleft_speed = -Math.PI/90; 
        let turnright_speed = Math.PI/90;
        let keydown = false;
        
        let vec;
        if (inputMap["w"] || inputMap["W"]) {
            vec = mesh.forward.scaleInPlace(forward_speed);
            mesh.moveWithCollisions(vec);
            keydown = true;
            walk_anim.play(true);
        }

        if (inputMap["s"] | inputMap["S"]) {
            mesh.moveWithCollisions(mesh.forward.scaleInPlace(backward_speed));
            keydown = true;
            walk_anim.play(true);
        }
        
        if (inputMap["a"] || inputMap["A"])
            mesh.rotate(BABYLON.Axis.Y, turnleft_speed);

        if (inputMap["d"] || inputMap["D"])
            mesh.rotate(BABYLON.Axis.Y, turnright_speed);
        
        if(keydown == false)
            walk_anim.stop();
        
        mesh.moveWithCollisions(new BABYLON.Vector3(0,-0.1,0.0));
       
    });
    
}

function ArcCam(scene, mesh){
    if(mesh!=null)arc_camera.lockedTarget = mesh;
    // 대상과의 거리
    arc_camera.radius = 3;
    arc_camera.wheelDeltaPercentage = 0.01; // 마우스휠 속도
    arc_camera.minZ = 0;    // 근거리 커팅 X
    camToggle(scene, arc_camera);
}

function camToggle(scene, camera){
    // 카메라 캔버스 연결
    if(currentCamera != camera)
    {
        if(currentCamera!=null) currentCamera.detachControl(canvas);
        camera.attachControl(canvas, true);
        currentCamera = camera;
        scene.activeCamera = camera;
    }   
}

const scene = createScene();

engine.runRenderLoop(function () {
    scene.render();
});

window.addEventListener("resize", function () {
    engine.resize();
});

그리고 케이군 캐릭터 모델 파일이 있어야 하는데요.
오랜만이고 하니 새로 올려 놓겠습니다.
아래 파일을 다운받아 같은 폴더에 저장해 주세요.

character01.glb
0.05MB

 

파일목록이 아래와 같으면 됩니다.

이제 웹페이지에 접근하면 재미있는 일이 일어나는데요.
케이군이 마치 과거 영화의 ( 람보인가? ) 한 장면처럼 바닥에서 솟구쳐 오를 겁니다.

이제 wasd 키보드를 이용해 케이군을 조작하여 이동할 수 있는데요.
현재 케이군은 큰 큐브 위에 우뚝 서 있는 상태입니다.

케이군의 몸은 중력이 적용되어 있는 상태이기 때문에
앞으로 이동하면 바닥으로 뚝 떨어집니다. 뭐 높긴 하지만 가상 공간이라 다치거나 그러지는 않구요.

나는 수퍼 케이맨!

만약 케이군을 사방끝으로 이동시켜 떨어뜨린다면 무슨 일이 일어날까요?

궁금하네?

저 아래쪽으로 한없이 한없이 떨어집니다.
아무래도 나중에 안전 장치를 마련해야 겠군요.
F5 새로고침하면 다시 돌아오니 미아가 될 걱정은 없습니다:)

하염없이 하염없이...

그 외에도 컬리전은 벽의 역활을 합니다.
케이군이 앞으로 가려 할때 컬리전이 적용된 벽을 만나면 더 이상 전진하지를 못하지요.
이 컬리전이란 걸로 앞으로 재미난걸 만들 수 있습니다.

가고싶어 갈 수 없고~


마무~리

바빌론js에서의 중력과 컬리젼, 유용하게 보셨나 모르겠습니다.
오늘도 방문해주셔서 감사!합니다!


다음 게시글 : https://itadventure.tistory.com/710

 

바빌론JS - 블랜더에서 만든거 불러오기

지난 게시글에서는 바빌론JS 의 중력에 대해 알아보았는데요.https://itadventure.tistory.com/709 바빌론JS - 중력과 컬리젼바빌론JS의 컬리젼(Collision)에 대해 알아보았는데요.바빌론JS에서 중력과 같은

itadventure.tistory.com