發(fā)布時(shí)間:2023-11-27 18:11:30 瀏覽量:176次
此案例實(shí)現(xiàn)了人物跟隨著移動(dòng)操作桿進(jìn)行移動(dòng)并執(zhí)行跑步動(dòng)作,右邊的攻擊按鈕可以實(shí)現(xiàn)攻擊,短時(shí)間內(nèi)連按可以實(shí)現(xiàn)不同的攻擊動(dòng)作。
在線查看:
https://jxtreehouse.github.io/three.js-lessions/%E6%95%99%E7%A8%8B/examples/12_game_operation.html
源碼倉(cāng)庫(kù):
https://github.com/JXtreehouse/three.js-lessions/blob/gh-pages/%E6%95%99%E7%A8%8B/examples/12_game_operation.html
首先,我們需要把舞臺(tái)搭建出來(lái),先創(chuàng)建scene場(chǎng)景:
我們創(chuàng)建了場(chǎng)景,并設(shè)置了場(chǎng)景一個(gè)灰色的背景色。還設(shè)置了場(chǎng)景的霧化效果,這個(gè)霧的效果主要是針對(duì)于場(chǎng)景的相機(jī)的距離實(shí)現(xiàn)的,三個(gè)值分別是霧的顏色、霧的開(kāi)始距離、完全霧化距離相機(jī)的位置。
我們創(chuàng)建了一個(gè)與地面呈45度角并朝向原點(diǎn)的相機(jī):
我們創(chuàng)建了兩個(gè)燈光:
我們使用平面幾何體創(chuàng)建了一個(gè)貼有草皮貼圖的材質(zhì)的模型:
到這里,場(chǎng)景、燈光、相機(jī)、舞臺(tái)都已經(jīng)備齊。接下來(lái)我們將請(qǐng)出我們主角naruto登場(chǎng)。
首先我們將模型導(dǎo)入到場(chǎng)景內(nèi),注意,案例中的模型比較大,加載和處理需要一定的時(shí)間,請(qǐng)小伙伴們耐心等待即可(實(shí)際案例里面可以加個(gè)loading動(dòng)畫):
var loader = new THREE.FBXLoader();
loader.load("http://www.toutiao.com/js/models/fbx/Naruto.fbx", function (mesh) {
scene.add(mesh);
});
我們不單單只是將模型添加到場(chǎng)景,還對(duì)模型的陰影和位置做了一下調(diào)整:
調(diào)整模型的位置,站立在草地上面
設(shè)置燈光一直照射模型:
這個(gè)模型里面含有27個(gè)骨骼動(dòng)畫,我們可以通過(guò)設(shè)置不同的動(dòng)畫,來(lái)實(shí)現(xiàn)一整套的動(dòng)作來(lái)實(shí)現(xiàn)相應(yīng)的比如攻擊效果,移動(dòng)效果等。接下來(lái)我們通過(guò)模型的數(shù)據(jù)生成一下所需的動(dòng)畫:
模型加載成功后,我們需要讓模型執(zhí)行一個(gè)普通的站立效果:
我們主要添加了兩種操作:模型位置移動(dòng)操作和攻擊效果。
操作按鈕為了方便,直接使用的dom標(biāo)簽?zāi)M出來(lái)的。 模型位置移動(dòng)操作中,我們需要模型的位置的變動(dòng)和模型的朝向以及修改站立動(dòng)畫和奔跑動(dòng)畫的切換。 攻擊效果則是實(shí)現(xiàn)攻擊并且根據(jù)點(diǎn)擊速度實(shí)現(xiàn)一整套的攻擊動(dòng)作切換。
在實(shí)現(xiàn)位置移動(dòng)效果中,我們?yōu)榘粹o綁定了三個(gè)事件:鼠標(biāo)按下,鼠標(biāo)移動(dòng),鼠標(biāo)抬起。 在鼠標(biāo)按下時(shí),我們獲取到了當(dāng)前操作圓盤的中心點(diǎn)的位置,讓模型進(jìn)入跑步動(dòng)畫,綁定了鼠標(biāo)的移動(dòng)和抬起事件。重要的是更新模型的移動(dòng)方向和移動(dòng)速度。
上面的dop類是封裝的一個(gè)兼容多端的事件庫(kù),github地址:
https://github.com/johnson2heng/dop 在鼠標(biāo)移動(dòng)回調(diào)事件中,我們更新模型的移動(dòng)方向和移動(dòng)速度。
function move(event) {
getRadian(event);
}
最后在鼠標(biāo)抬起事件中,我們解綁事件,將按鍵復(fù)原,并停止掉模型的移動(dòng)狀態(tài),將模型動(dòng)畫恢復(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è)置移動(dòng)距離為零
characterMove(new THREE.Vector2(), 0);
//鼠標(biāo)抬起切換站立狀態(tài)
state.skills === 0 && gui["action" + 24]();
}
三個(gè)事件綁定完成后,我們需要將在回調(diào)中獲得的值求出當(dāng)前的偏轉(zhuǎn)方向和移動(dòng)速度: 首先我們獲取一下當(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ù)位置求出距離操作圓盤中心的位置,并保證最大值也不會(huì)超出圓盤的半徑:
let distance = center.distanceTo(mouse);
distance >= parseFloat(dop.getFinalStyle(control, "width")) / 2 && (distance = parseFloat(dop.getFinalStyle(control, "width")) / 2);
計(jì)算出來(lái)當(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)的距離。
//修改當(dāng)前的移動(dòng)方向和移動(dòng)速度
characterMove(mouse.normalize(), distance / (parseFloat(dop.getFinalStyle(control, "width")) / 2));
接下來(lái)我們查看一下characterMove方法,在這個(gè)方法中,我們計(jì)算出了模型每一幀需要移動(dòng)的距離。這里有一個(gè)問(wèn)題,我們所謂的操作桿向前讓模型移動(dòng)前方,其實(shí)是相機(jī)朝向的前方。所以我們需要先求出相機(jī)的前方矢量,再通過(guò)相機(jī)的前方矢量為基礎(chǔ),計(jì)算出來(lái)模型實(shí)際方向。 我們首先聲明了兩個(gè)變量,一個(gè)是旋轉(zhuǎn)矩陣,另一個(gè)是移動(dòng)矢量:
let direction = new THREE.Matrix4(); //當(dāng)前移動(dòng)的旋轉(zhuǎn)矩陣
let move = new THREE.Vector3(); //當(dāng)前位置移動(dòng)的距離
在characterMove函數(shù)內(nèi),我們根據(jù)相機(jī)的四元數(shù)獲得了旋轉(zhuǎn)矩陣:
/重置矩陣
direction.identity();
//通過(guò)相機(jī)的四元數(shù)獲取到相機(jī)的旋轉(zhuǎn)矩陣
let quaternion = camera.quaternion;
direction.makeRotationFromQuaternion(quaternion);
然后通過(guò)旋轉(zhuǎn)矩陣和當(dāng)前的操作桿的方向通過(guò)相乘計(jì)算出來(lái)實(shí)際模型移動(dòng)的方向:
//獲取到操作桿的移動(dòng)方向
move.x = vector.x;
move.y = 0;
move.z = vector.y;
//通過(guò)相機(jī)方向和操作桿獲得最終角色的移動(dòng)方向
move.applyMatrix4(direction);
move.normalize();
最后,通過(guò)比例和方向得出當(dāng)前模型每一幀移動(dòng)的距離,因?yàn)槲覀儾恍枰薷哪P蛓軸,所以實(shí)際上也只是修改兩個(gè)軸的位置:
move.x = move.x * ratio * 10;
move.z = move.z * ratio * 10;
我們獲取到了模型的每一幀移動(dòng)的距離,還需要在幀循環(huán)中調(diào)用:
//如果模型添加成功,則每幀都移動(dòng)角色位置
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ī)都會(huì)跟隨移動(dòng),實(shí)現(xiàn)了,我們上面動(dòng)圖中的模型移動(dòng)的效果。
在實(shí)現(xiàn)攻擊效果時(shí),我沒(méi)有只是簡(jiǎn)單的實(shí)現(xiàn)一個(gè)普通的攻擊,而是直接實(shí)現(xiàn)一套連招。 這一套連招是通過(guò)五個(gè)動(dòng)作組成,在執(zhí)行一個(gè)攻擊動(dòng)畫時(shí)如果再次點(diǎn)擊了攻擊按鈕,執(zhí)行完這個(gè)攻擊動(dòng)畫將不會(huì)切換到站立動(dòng)畫,而是直接切換到連招的下一個(gè)攻擊動(dòng)畫中。 只要連續(xù)點(diǎn)按攻擊按鈕,模型將完成一整套的動(dòng)作。實(shí)現(xiàn)這個(gè)效果,我們只是使用了一個(gè)簡(jiǎn)單的定時(shí)器即可實(shí)現(xiàn),接下來(lái)我們通過(guò)代碼了解一下實(shí)現(xiàn)過(guò)程。
在實(shí)現(xiàn)動(dòng)畫前,先設(shè)置一個(gè)連招的數(shù)組,將需要的動(dòng)作添加到數(shù)組當(dāng)中。我這里添加了五個(gè)手部攻擊的效果:
let attackList = [12, 13, 14, 15, 16]; //連招的循序
let attackCombo = false; //是否連招,接下一個(gè)攻擊
我們還設(shè)置了attackCombo設(shè)置當(dāng)前是否可以連招的變量,這個(gè)變量state.skills值不為0時(shí),將變?yōu)閠rue。定時(shí)器每一次更新的時(shí)候,將判斷attackCombo是否為true,在為true的狀態(tài)下,將執(zhí)行連招的下一個(gè)動(dòng)作。否則,將停止連招。
//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è)置了連招,上一個(gè)攻擊動(dòng)作完成后,進(jìn)行下一個(gè)攻擊動(dòng)作
state.skills++;
//如果整套攻擊動(dòng)作已經(jīng)執(zhí)行完成,則清除定時(shí)器
if(state.skills-1 >= attackList.length){
closeAttack();
return;
}
//進(jìn)行下一個(gè)動(dòng)作
gui["action" + attackList[state.skills-1]]();
attackCombo = false;
}
else{
closeAttack();
}
}, naruto.animations[attackList[state.skills-1]].duration*1000);
}
else{
attackCombo = true;
}
在關(guān)閉掉攻擊動(dòng)畫的函數(shù)內(nèi),我們首先將state.skills設(shè)置為0,然后恢復(fù)到移動(dòng)或者站立動(dòng)畫,最后清除掉定時(shí)器:
//關(guān)閉攻擊狀態(tài)
function closeAttack() {
state.skills = 0;
//根據(jù)狀態(tài)設(shè)置是移動(dòng)狀態(tài)還是站立狀態(tài)
state.move ? gui["action" + 3]() :gui["action" + 24](); //回到站立狀態(tài)
clearInterval(attackInterval);
}
通過(guò)很簡(jiǎn)單的一些代碼,我們就實(shí)現(xiàn)了一個(gè)復(fù)雜的連招效果。是不是很有成就感,這就是在最前面看到的那個(gè)操作gif的效果的案例
Fog類定義的是線性霧,霧的密度是隨著距離線性增大的,即場(chǎng)景中物體霧化效果隨著隨距離線性變化。
構(gòu)造函數(shù)霧Fog(color,near,far)的三個(gè)參數(shù)分別對(duì)應(yīng)霧對(duì)象Fog的三個(gè)屬性.color、.near和.far。
.color屬性表示霧的顏色,比如設(shè)置為紅色,場(chǎng)景中遠(yuǎn)處物體為黑色,場(chǎng)景中最近處距離物體是自身顏色,最遠(yuǎn)和最近之間的物體顏色是物體本身顏色和霧顏色的混合效果。
// 改變霧的顏色為白色
scene.fog.color.set(0xffffff)
.near屬性值表示應(yīng)用霧化效果的最小距離,距離活動(dòng)攝像機(jī)長(zhǎng)度小于.near的物體將不會(huì)被霧所影響
.far屬性值表示應(yīng)用霧化效果的最大距離,距離活動(dòng)攝像機(jī)長(zhǎng)度大于.far的物體將不會(huì)被霧所影響
透視投影照相機(jī)(Perspective Camera)的構(gòu)造函數(shù)是:
THREE.PerspectiveCamera(fov, aspect, near, far)
透視圖中,灰色的部分是視景體,是可能被渲染的物體所在的區(qū)域。fov是視景體豎直方向上的張角(是角度制而非弧度制),如側(cè)視圖所示。
aspect等于width / height,是照相機(jī)水平方向和豎直方向長(zhǎng)度的比值,通常設(shè)為Canvas的橫縱比例。
near和far分別是照相機(jī)到視景體最近、最遠(yuǎn)的距離,均為正值,且far應(yīng)大于near。
環(huán)境光是指場(chǎng)景整體的光照效果,是由于場(chǎng)景內(nèi)若干光源的多次反射形成的亮度一致的效果,通常用來(lái)為整個(gè)場(chǎng)景指定一個(gè)基礎(chǔ)亮度。因此,環(huán)境光沒(méi)有明確的光源位置,在各處形成的亮度也是一致的。
在設(shè)置環(huán)境光時(shí),只需要指定光的顏色:
THREE.AmbientLight(hex)
其中,hex是十六進(jìn)制的RGB顏色信息,如紅色表示為0xff0000。
創(chuàng)建環(huán)境光并將其添加到場(chǎng)景中的完整做法是:
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
如果此時(shí)場(chǎng)景中沒(méi)有物體,只添加了這個(gè)環(huán)境光,那么渲染的結(jié)果仍然是一片黑
環(huán)境光通常使用白色或者灰色,作為整體光照的基礎(chǔ)。
##TextureLoader
通過(guò)紋理貼圖加載器TextureLoader的load()方法加載一張圖片可以返回一個(gè)紋理對(duì)象Texture,紋理對(duì)象Texture可以作為模型材質(zhì)顏色貼圖.map屬性的值。
材質(zhì)的顏色貼圖屬性.map設(shè)置后,模型會(huì)從紋理貼圖上采集像素值,這時(shí)候一般來(lái)說(shuō)不需要在設(shè)置材質(zhì)顏色.color。.map貼圖之所以稱之為顏色貼圖就是因?yàn)榫W(wǎng)格模型會(huì)獲得顏色貼圖的顏色值RGB。
three.js有官方的fbx插件,可以直接將模型加載至網(wǎng)頁(yè),并且支持動(dòng)畫數(shù)據(jù),代碼量也是最少的。 但是,該格式存在很大弊端:插件對(duì)文件格式的規(guī)范很嚴(yán)格,換言之,插件支持性不太好。從網(wǎng)上下載的fbx動(dòng)畫,十有八九會(huì)加載失敗。
首先需要引入FBXLoader.js插件,如果報(bào)錯(cuò) “Error: THREE.FBXLoader: External library Inflate.min.js required, obtain or import from
https://github.com/imaya/zlib.js”,則還需引入inflate.min.js文件。
我們可以看一個(gè)簡(jiǎn)單案例
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í)心得與知識(shí)分享
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
熱門資訊
探討游戲引擎的文章,介紹了10款游戲引擎及其代表作品,涵蓋了RAGE Engine、Naughty Dog Game Engine、The Dead Engine、Cry Engine、Avalanche Engine、Anvil Engine、IW Engine、Frostbite Engine、Creation引擎、Unreal Engine等引擎。借此分析引出了游戲設(shè)計(jì)領(lǐng)域和數(shù)字藝術(shù)教育的重要性,歡迎點(diǎn)擊咨詢報(bào)名。
2. 手機(jī)游戲如何開(kāi)發(fā)(如何制作傳奇手游,都需要準(zhǔn)備些什么?)
?如何制作傳奇手游,都需要準(zhǔn)備些什么?提到傳奇手游相信大家都不陌生,他是許多80、90后的回憶;從起初的端游到現(xiàn)在的手游,說(shuō)明時(shí)代在進(jìn)步游戲在更新,更趨于方便化移動(dòng)化。而如果我們想要制作一款傳奇手游的
3. B站視頻剪輯軟件「必剪」:免費(fèi)、炫酷特效,小白必備工具
B站視頻剪輯軟件「必剪」,完全免費(fèi)、一鍵制作炫酷特效,適合新手小白。快來(lái)試試!
4. Steam值得入手的武俠游戲盤點(diǎn),各具特色的快意江湖
游戲中玩家將面臨武俠人生的掙扎抉擇,戰(zhàn)或降?殺或放?每個(gè)抉定都將觸發(fā)更多愛(ài)恨糾葛的精彩奇遇?!短烀嬗肪哂卸嗑€劇情多結(jié)局,不限主線發(fā)展,高自由...
5. Bigtime加密游戲經(jīng)濟(jì)體系揭秘,不同玩家角色的經(jīng)濟(jì)活動(dòng)
Bigtime加密游戲經(jīng)濟(jì)模型分析,探討游戲經(jīng)濟(jì)特點(diǎn),幫助玩家更全面了解這款GameFi產(chǎn)品。
6. 3D動(dòng)畫軟件你知道幾個(gè)?3ds Max、Blender、Maya、Houdini大比拼
當(dāng)提到3D動(dòng)畫軟件或動(dòng)畫工具時(shí),指的是數(shù)字內(nèi)容創(chuàng)建工具。它是用于造型、建模以及繪制3D美術(shù)動(dòng)畫的軟件程序。但是,在3D動(dòng)畫軟件中還包含了其他類型的...
7. 3D動(dòng)漫建模全過(guò)程,不是一般人能學(xué)的會(huì)的,會(huì)的多不是人?
步驟01:面部,頸部,身體在一起這次我不準(zhǔn)備設(shè)計(jì)圖片,我從雕刻進(jìn)入。這一次,它將是一種純粹關(guān)注建模而非整體繪畫的形式。像往常一樣,我從Sphere創(chuàng)建它...
8. 如何自己開(kāi)發(fā)一款游戲(游戲開(kāi)發(fā)入門必看:五大獨(dú)立游戲開(kāi)發(fā)技巧)
?游戲開(kāi)發(fā)入門必看:五大獨(dú)立游戲開(kāi)發(fā)技巧無(wú)論您是剛剛起步開(kāi)發(fā)自己的第一款游戲,還是已經(jīng)制作了幾款游戲,本篇文章中的5大獨(dú)立游戲開(kāi)發(fā)技巧都可以幫助您更好地設(shè)計(jì)下一款游戲。無(wú)論你對(duì)游戲有著什么樣的概念,都
9. 開(kāi)發(fā)三昧游戲叫什么(三昧動(dòng)漫)
?三昧動(dòng)漫對(duì)于著名ARPG游戲《巫師》系列,最近CD Projekt 的高層回應(yīng)并不會(huì)推出《巫師4》。因?yàn)椤段讕煛废盗性诓邉澋臅r(shí)候一直定位在“三部曲”的故事框架,所以在游戲的出品上不可能出現(xiàn)《巫師4》
10. 3D打印技巧揭秘!Cura設(shè)置讓你的模型更堅(jiān)固
想讓你的3D打印模型更堅(jiān)固?不妨嘗試一下Cura參數(shù)設(shè)置和設(shè)計(jì)技巧,讓你輕松掌握!
最新文章
同學(xué)您好!