激情六月丁香婷婷|亚洲色图AV二区|丝袜AV日韩AV|久草视频在线分类|伊人九九精品视频|国产精品一级电影|久草视频在线99|在线看的av网址|伊人99精品无码|午夜无码视频在线

高校合作1:010-59833514 ?咨詢電話:400-810-1418 服務(wù)與監(jiān)督電話:400-810-1418轉(zhuǎn)接2

threejs開發(fā)游戲(游戲編程)

發(fā)布時間:2023-11-27 18:11:30 瀏覽量:172次

?游戲編程

threejs開發(fā)游戲(游戲編程)

此案例實(shí)現(xiàn)了人物跟隨著移動操作桿進(jìn)行移動并執(zhí)行跑步動作,右邊的攻擊按鈕可以實(shí)現(xiàn)攻擊,短時間內(nèi)連按可以實(shí)現(xiàn)不同的攻擊動作。

在線查看:
https://jxtreehouse.github.io/three.js-lessions/%E6%95%99%E7%A8%8B/examples/12_game_operation.html

源碼倉庫:
https://github.com/JXtreehouse/three.js-lessions/blob/gh-pages/%E6%95%99%E7%A8%8B/examples/12_game_operation.html

首先,我們需要把舞臺搭建出來,先創(chuàng)建scene場景:

我們創(chuàng)建了場景,并設(shè)置了場景一個灰色的背景色。還設(shè)置了場景的霧化效果,這個霧的效果主要是針對于場景的相機(jī)的距離實(shí)現(xiàn)的,三個值分別是霧的顏色、霧的開始距離、完全霧化距離相機(jī)的位置。

我們創(chuàng)建了一個與地面呈45度角并朝向原點(diǎn)的相機(jī):

我們創(chuàng)建了兩個燈光:

  • 照射全局的環(huán)境光 THREE.AmbientLight
  • 可以產(chǎn)生陰影的平衡光 THREE.DirectionalLight

我們使用平面幾何體創(chuàng)建了一個貼有草皮貼圖的材質(zhì)的模型:

到這里,場景、燈光、相機(jī)、舞臺都已經(jīng)備齊。接下來我們將請出我們主角naruto登場。

首先我們將模型導(dǎo)入到場景內(nèi),注意,案例中的模型比較大,加載和處理需要一定的時間,請小伙伴們耐心等待即可(實(shí)際案例里面可以加個loading動畫):

var loader = new THREE.FBXLoader();
        loader.load("http://www.toutiao.com/js/models/fbx/Naruto.fbx", function (mesh) {
            scene.add(mesh);
        });

我們不單單只是將模型添加到場景,還對模型的陰影和位置做了一下調(diào)整:

調(diào)整模型的位置,站立在草地上面

設(shè)置燈光一直照射模型:


這個模型里面含有27個骨骼動畫,我們可以通過設(shè)置不同的動畫,來實(shí)現(xiàn)一整套的動作來實(shí)現(xiàn)相應(yīng)的比如攻擊效果,移動效果等。接下來我們通過模型的數(shù)據(jù)生成一下所需的動畫:

模型加載成功后,我們需要讓模型執(zhí)行一個普通的站立效果:

我們主要添加了兩種操作:模型位置移動操作和攻擊效果。

操作按鈕為了方便,直接使用的dom標(biāo)簽?zāi)M出來的。 模型位置移動操作中,我們需要模型的位置的變動和模型的朝向以及修改站立動畫和奔跑動畫的切換。 攻擊效果則是實(shí)現(xiàn)攻擊并且根據(jù)點(diǎn)擊速度實(shí)現(xiàn)一整套的攻擊動作切換。

在實(shí)現(xiàn)位置移動效果中,我們?yōu)榘粹o綁定了三個事件:鼠標(biāo)按下,鼠標(biāo)移動,鼠標(biāo)抬起。 在鼠標(biāo)按下時,我們獲取到了當(dāng)前操作圓盤的中心點(diǎn)的位置,讓模型進(jìn)入跑步動畫,綁定了鼠標(biāo)的移動和抬起事件。重要的是更新模型的移動方向和移動速度。


上面的dop類是封裝的一個兼容多端的事件庫,github地址:
https://github.com/johnson2heng/dop 在鼠標(biāo)移動回調(diào)事件中,我們更新模型的移動方向和移動速度。

function move(event) {
    getRadian(event);
}

最后在鼠標(biāo)抬起事件中,我們解綁事件,將按鍵復(fù)原,并停止掉模型的移動狀態(tài),將模型動畫恢復(fù)到站立狀態(tài)。

function up() {
    doc.remove("move", move);
    doc.remove("up", up);

    //按鈕復(fù)原
    bar.style.marginTop = 0;
    barWrap.style.transform = `translate(-50%, -50%) rotate(0deg)`;
    bar.style.transform = `translate(-50%, -50%) rotate(0deg)`;

    //設(shè)置移動距離為零
    characterMove(new THREE.Vector2(), 0);

    //鼠標(biāo)抬起切換站立狀態(tài)
    state.skills === 0 && gui["action" + 24]();
}

三個事件綁定完成后,我們需要將在回調(diào)中獲得的值求出當(dāng)前的偏轉(zhuǎn)方向和移動速度: 首先我們獲取一下當(dāng)前鼠標(biāo)的位置:

if (media === "pc") {
    mouse.x = event.clientX;
    mouse.y = event.clientY;
}
else {
    mouse.x = event.touches[0].clientX;
    mouse.y = event.touches[0].clientY;
}

根據(jù)位置求出距離操作圓盤中心的位置,并保證最大值也不會超出圓盤的半徑:

let distance = center.distanceTo(mouse);
distance >= parseFloat(dop.getFinalStyle(control, "width")) / 2 && (distance = parseFloat(dop.getFinalStyle(control, "width")) / 2);

計(jì)算出來當(dāng)前位置和中心的夾角,并修改dom的位置:


//計(jì)算兩點(diǎn)之間的夾角
mouse.x = mouse.x - center.x;
mouse.y = mouse.y - center.y;

//修改操作桿的css樣式
bar.style.marginTop = `-${distance}px`;
bar.style.transform = `translate(-50%, -50%) rotate(-${(mouse.angle() / Math.PI * 180 + 90) % 360}deg)`;
barWrap.style.transform = `translate(-50%, -50%) rotate(${(mouse.angle() / Math.PI * 180 + 90) % 360}deg)`;

函數(shù)的最后,則調(diào)用的characterMove方法,將按鈕數(shù)據(jù)轉(zhuǎn)換成為模型實(shí)際需要移動的距離。

//修改當(dāng)前的移動方向和移動速度
characterMove(mouse.normalize(), distance / (parseFloat(dop.getFinalStyle(control, "width")) / 2));

接下來我們查看一下characterMove方法,在這個方法中,我們計(jì)算出了模型每一幀需要移動的距離。這里有一個問題,我們所謂的操作桿向前讓模型移動前方,其實(shí)是相機(jī)朝向的前方。所以我們需要先求出相機(jī)的前方矢量,再通過相機(jī)的前方矢量為基礎(chǔ),計(jì)算出來模型實(shí)際方向。 我們首先聲明了兩個變量,一個是旋轉(zhuǎn)矩陣,另一個是移動矢量:

let direction = new THREE.Matrix4(); //當(dāng)前移動的旋轉(zhuǎn)矩陣
let move = new THREE.Vector3(); //當(dāng)前位置移動的距離

在characterMove函數(shù)內(nèi),我們根據(jù)相機(jī)的四元數(shù)獲得了旋轉(zhuǎn)矩陣:

/重置矩陣
direction.identity();

//通過相機(jī)的四元數(shù)獲取到相機(jī)的旋轉(zhuǎn)矩陣
let quaternion = camera.quaternion;
direction.makeRotationFromQuaternion(quaternion);

然后通過旋轉(zhuǎn)矩陣和當(dāng)前的操作桿的方向通過相乘計(jì)算出來實(shí)際模型移動的方向:


//獲取到操作桿的移動方向
move.x = vector.x;
move.y = 0;
move.z = vector.y;

//通過相機(jī)方向和操作桿獲得最終角色的移動方向
move.applyMatrix4(direction);
move.normalize();

最后,通過比例和方向得出當(dāng)前模型每一幀移動的距離,因?yàn)槲覀儾恍枰薷哪P蛓軸,所以實(shí)際上也只是修改兩個軸的位置:

move.x = move.x * ratio * 10;
move.z = move.z * ratio * 10;

我們獲取到了模型的每一幀移動的距離,還需要在幀循環(huán)中調(diào)用:

//如果模型添加成功,則每幀都移動角色位置
if (naruto) {
    //獲取當(dāng)前位置
    position.x += move.x;
    position.z += move.z;

    //修改模型位置
    naruto.position.x = position.x;
    naruto.position.z = position.z;

    //修改平衡光的位置
    light.position.x = position.x;
    light.position.z = position.z + 100;

    //修改相機(jī)位置
    camera.position.x = position.x;
    camera.position.z = position.z - 800;
}


當(dāng)前的模型,燈光,和相機(jī)都會跟隨移動,實(shí)現(xiàn)了,我們上面動圖中的模型移動的效果。

在實(shí)現(xiàn)攻擊效果時,我沒有只是簡單的實(shí)現(xiàn)一個普通的攻擊,而是直接實(shí)現(xiàn)一套連招。 這一套連招是通過五個動作組成,在執(zhí)行一個攻擊動畫時如果再次點(diǎn)擊了攻擊按鈕,執(zhí)行完這個攻擊動畫將不會切換到站立動畫,而是直接切換到連招的下一個攻擊動畫中。 只要連續(xù)點(diǎn)按攻擊按鈕,模型將完成一整套的動作。實(shí)現(xiàn)這個效果,我們只是使用了一個簡單的定時器即可實(shí)現(xiàn),接下來我們通過代碼了解一下實(shí)現(xiàn)過程。

在實(shí)現(xiàn)動畫前,先設(shè)置一個連招的數(shù)組,將需要的動作添加到數(shù)組當(dāng)中。我這里添加了五個手部攻擊的效果:

let attackList = [12, 13, 14, 15, 16]; //連招的循序
let attackCombo = false; //是否連招,接下一個攻擊

我們還設(shè)置了attackCombo設(shè)置當(dāng)前是否可以連招的變量,這個變量state.skills值不為0時,將變?yōu)閠rue。定時器每一次更新的時候,將判斷attackCombo是否為true,在為true的狀態(tài)下,將執(zhí)行連招的下一個動作。否則,將停止連招。

//attackIndex 等于0,當(dāng)前不處于攻擊狀態(tài)  不等于,當(dāng)前處于攻擊狀態(tài)
if(state.skills === 0){
    state.skills++;
    gui["action" + attackList[state.skills-1]]();
    attackInterval = setInterval(function () {
        if(attackCombo){
            //如果設(shè)置了連招,上一個攻擊動作完成后,進(jìn)行下一個攻擊動作
            state.skills++;
            //如果整套攻擊動作已經(jīng)執(zhí)行完成,則清除定時器
            if(state.skills-1 >= attackList.length){
                closeAttack();
                return;
            }

            //進(jìn)行下一個動作
            gui["action" + attackList[state.skills-1]]();

            attackCombo = false;
        }
        else{
            closeAttack();
        }
    }, naruto.animations[attackList[state.skills-1]].duration*1000);
}
else{
    attackCombo = true;
}

在關(guān)閉掉攻擊動畫的函數(shù)內(nèi),我們首先將state.skills設(shè)置為0,然后恢復(fù)到移動或者站立動畫,最后清除掉定時器:

threejs開發(fā)游戲(游戲編程)


//關(guān)閉攻擊狀態(tài)
function closeAttack() {
    state.skills = 0;
    //根據(jù)狀態(tài)設(shè)置是移動狀態(tài)還是站立狀態(tài)
    state.move ? gui["action" + 3]() :gui["action" + 24](); //回到站立狀態(tài)
    clearInterval(attackInterval);
}

通過很簡單的一些代碼,我們就實(shí)現(xiàn)了一個復(fù)雜的連招效果。是不是很有成就感,這就是在最前面看到的那個操作gif的效果的案例

  • OrbitControls.js: 允許我們使用鼠標(biāo)或觸摸屏瀏覽操作3D場景

Fog類定義的是線性霧,霧的密度是隨著距離線性增大的,即場景中物體霧化效果隨著隨距離線性變化。

構(gòu)造函數(shù)霧Fog(color,near,far)的三個參數(shù)分別對應(yīng)霧對象Fog的三個屬性.color、.near和.far。

.color屬性表示霧的顏色,比如設(shè)置為紅色,場景中遠(yuǎn)處物體為黑色,場景中最近處距離物體是自身顏色,最遠(yuǎn)和最近之間的物體顏色是物體本身顏色和霧顏色的混合效果。

// 改變霧的顏色為白色
scene.fog.color.set(0xffffff)

.near屬性值表示應(yīng)用霧化效果的最小距離,距離活動攝像機(jī)長度小于.near的物體將不會被霧所影響

.far屬性值表示應(yīng)用霧化效果的最大距離,距離活動攝像機(jī)長度大于.far的物體將不會被霧所影響

透視投影照相機(jī)(Perspective Camera)的構(gòu)造函數(shù)是:

THREE.PerspectiveCamera(fov, aspect, near, far)

透視圖中,灰色的部分是視景體,是可能被渲染的物體所在的區(qū)域。fov是視景體豎直方向上的張角(是角度制而非弧度制),如側(cè)視圖所示。

aspect等于width / height,是照相機(jī)水平方向和豎直方向長度的比值,通常設(shè)為Canvas的橫縱比例。

near和far分別是照相機(jī)到視景體最近、最遠(yuǎn)的距離,均為正值,且far應(yīng)大于near。

環(huán)境光是指場景整體的光照效果,是由于場景內(nèi)若干光源的多次反射形成的亮度一致的效果,通常用來為整個場景指定一個基礎(chǔ)亮度。因此,環(huán)境光沒有明確的光源位置,在各處形成的亮度也是一致的。

在設(shè)置環(huán)境光時,只需要指定光的顏色:

THREE.AmbientLight(hex)

其中,hex是十六進(jìn)制的RGB顏色信息,如紅色表示為0xff0000。

創(chuàng)建環(huán)境光并將其添加到場景中的完整做法是:

var light = new THREE.AmbientLight(0xffffff);
scene.add(light);

如果此時場景中沒有物體,只添加了這個環(huán)境光,那么渲染的結(jié)果仍然是一片黑

環(huán)境光通常使用白色或者灰色,作為整體光照的基礎(chǔ)。

##TextureLoader

通過紋理貼圖加載器TextureLoader的load()方法加載一張圖片可以返回一個紋理對象Texture,紋理對象Texture可以作為模型材質(zhì)顏色貼圖.map屬性的值。

材質(zhì)的顏色貼圖屬性.map設(shè)置后,模型會從紋理貼圖上采集像素值,這時候一般來說不需要在設(shè)置材質(zhì)顏色.color。.map貼圖之所以稱之為顏色貼圖就是因?yàn)榫W(wǎng)格模型會獲得顏色貼圖的顏色值RGB。

three.js有官方的fbx插件,可以直接將模型加載至網(wǎng)頁,并且支持動畫數(shù)據(jù),代碼量也是最少的。 但是,該格式存在很大弊端:插件對文件格式的規(guī)范很嚴(yán)格,換言之,插件支持性不太好。從網(wǎng)上下載的fbx動畫,十有八九會加載失敗。

首先需要引入FBXLoader.js插件,如果報(bào)錯 “Error: THREE.FBXLoader: External library Inflate.min.js required, obtain or import from
https://github.com/imaya/zlib.js”,則還需引入inflate.min.js文件。

我們可以看一個簡單案例

https://wow.techbrood.com/fiddle/55419

The Making of “The Aviator”: Animating a Basic 3D Scene with Three.js : 使用three.js設(shè)計(jì)游戲的學(xué)習(xí)心得與知識分享

Three.js Making a Game

16 Three.js 游戲操作案例

Joystick, gamepad or 3D mouse support in Three.js

yoannmoinet/nipplejs

https://css-tricks.com/how-to-make-a-smartphone-controlled-3d-web-game/

ADDING SUPPORT FOR VR INPUTS WITH WEBXR AND THREE.JS

threejs開發(fā)游戲(游戲編程)

熱門課程推薦

熱門資訊

請綁定手機(jī)號

x

同學(xué)您好!

您已成功報(bào)名0元試學(xué)活動,老師會在第一時間與您取得聯(lián)系,請保持電話暢通!
確定