Threejs系列--22游戏开发--沙漠赛车游戏【布置游戏开始场景】
序言
本章将进行游戏开始场景的处理,场景中模型较多,需要统一管理
目录结构
资源目录里面的结构不变,点击传送门快速查看。
|__src
|__assets
|__js
| |__base 基础类文件夹
| |__Camera.js 相机类
| |__Resources.js 构建模型的资源类
| |__geometries 定制的物体类文件夹
| |__AreaFenceBufferGeometry.js 栏栅模型
| |__AreaFloorBorderBufferGeometry.js 进度条几何体
| |__materials 材质类文件夹
| |__Floor.js 地面材质
| |__AreaFence.js 【新增--开始按钮的栏栅材质】
| |__AreaFloorBorder.js 进度条着色器
| |__FloorShadow.js
| |__Matcap.js
| |__passes 合成器通道文件夹
| |__Blur.js 模糊着色器
| |__Glows.js 发光着色器
| |__utils 工具类文件夹
| |__Sizes.js 画布大小控制类
| |__EventEmitter.js 基础事件处理器
| |__Time.js 动画刷新
| |__Loader.js 加载器
| |__world 精灵类文件夹
| |__sections
| |__IntroSection.js 开始场景模型 【新增】
| |__Area.js 区域基础类
| |__Areas.js 区域管理类
| |__index.js 精灵类
| |__Floor.js 地面类
| |__Materials.js 材质类 【新增】
| |__Objects.js 模型管理类 【新增】
| |__Sounds.js 音乐加载类
| |__Application.js 初始化游戏的文件
|__index.js 入口
|__index.css 小项目,样式一丢丢
代码一览
index.js代码
...
import Objects from "./Objects.js";
import Materials from "./Materials.js";
import IntroSection from "./sections/IntroSection.js";
export default class {
...
start() {
//准备场景与游戏主场景的过渡动画
this.setReveal();
//场景中材质处理
this.setMaterials();
//场景中模型管理
this.setObjects();
//开始场景中各个模型加载
this.setSections();
}
setStartingScreen() {
...
this.startingScreen.area.on('interact', () => {
...
this.start(); //点击开始按钮后,开始布置游戏场景
//主场景展示动画
window.setTimeout(() => {
this.reveal.go();
}, 600);
})
}
setReveal(){
this.reveal = {};
this.reveal.matcapsProgress = 0;
this.reveal.previousMatcapsProgress = null;
this.reveal.go = () => {
gsap.fromTo(
this.reveal,
{ matcapsProgress: 0 },
{ matcapsProgress: 1, duration: 3 }
);
}
this.time.on("tick", () => {
if (this.reveal.matcapsProgress !== this.reveal.previousMatcapsProgress) {
for (const _materialKey in this.materials.shades.items) {
const material = this.materials.shades.items[_materialKey];
material.uniforms.uRevealProgress.value = this.reveal.matcapsProgress;
}
}
});
}
setMaterials(){
this.materials = new Materials({
time: this.time,
resources: this.resources
});
}
setObjects(){
this.objects = new Objects({
materials: this.materials,
});
this.container.add(this.objects.container);
}
setSections(){
this.sections = {};
const options = {
resources: this.resources,
objects: this.objects,
};
//加载开始场景
this.sections.intro = new IntroSection({
...options,
});
this.container.add(this.sections.intro.container);
}
}
Objects.js代码
import * as THREE from "three";
export default class Objects {
constructor(_options) {
this.materials = _options.materials;
this.container = new THREE.Object3D();
this.container.matrixAutoUpdate = false;
this.items = [];
this.floorShadows = [];
//设置模型的处理方式
this.setParsers();
}
setParsers() {
this.parsers = {};
this.parsers.items = [
{
regex: /^shade([a-z]+)_?[0-9]{0,3}?/i,
apply: (_mesh, _options) => {
const match = _mesh.name.match(/^shade([a-z]+)_?[0-9]{0,3}?/i);
const materialName = `${match[1]
.substring(0, 1)
.toLowerCase()}${match[1].substring(1)}`;
let material = this.materials.shades.items[materialName];
if (typeof material === "undefined") {
material = new THREE.MeshNormalMaterial();
}
const mesh = _options.duplicated ? _mesh.clone() : _mesh;
mesh.material = material;
if (mesh.children.length) {
for (const _child of mesh.children) {
if (_child instanceof THREE.Mesh) {
_child.material = material;
}
}
}
return mesh;
},
},
{
regex: /^floor_?[0-9]{0,3}?/i,
apply: (_mesh, _options) => {
const geometry = new THREE.PlaneBufferGeometry(
_mesh.scale.x,
_mesh.scale.y,
10,
10
);
const material = this.materials.items.floorShadow.clone();
material.uniforms.tShadow.value = _options.floorShadowTexture;
material.uniforms.uShadowColor.value = new THREE.Color(
this.materials.items.floorShadow.shadowColor
);
material.uniforms.uAlpha.value = 0;
const mesh = new THREE.Mesh(geometry, material);
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
this.floorShadows.push(mesh);
return mesh;
},
},
];
this.parsers.default = {};
this.parsers.default.apply = (_mesh) => {
const mesh = _mesh.clone();
mesh.material = this.materials.shades.items.white;
return mesh;
};
}
//合并模型
getConvertedMesh(_children, _options = {}) {
const container = new THREE.Object3D();
const center = new THREE.Vector3();
const baseChildren = [..._children];
for (const _child of baseChildren) {
if (_child.name.match(/^center_?[0-9]{0,3}?/i)) {
center.set(_child.position.x, _child.position.y, _child.position.z);
}
if (_child instanceof THREE.Mesh) {
let parser = this.parsers.items.find((_item) =>
_child.name.match(_item.regex)
);
if (typeof parser === "undefined") {
parser = this.parsers.default;
}
const mesh = parser.apply(_child, _options);
container.add(mesh);
}
}
if (center.length() > 0) {
for (const _child of container.children) {
_child.position.sub(center);
}
container.position.add(center);
}
if (_options.mass && _options.mass === 0) {
container.matrixAutoUpdate = false;
container.updateMatrix();
}
return container;
}
//添加模型
add(_options) {
const object = {};
object.merged = false;
object.shouldMerge = _options.mass === 0;
const offset = new THREE.Vector3();
if (_options.offset) {
offset.copy(_options.offset);
}
const rotation = new THREE.Euler();
if (_options.rotation) {
rotation.copy(_options.rotation);
}
const sleep = typeof _options.sleep === "undefined" ? true : _options.sleep;
object.container = this.getConvertedMesh(_options.base.children, _options);
object.container.position.copy(offset);
object.container.rotation.copy(rotation);
this.container.add(object.container);
if (_options.mass === 0) {
object.container.matrixAutoUpdate = false;
object.container.updateMatrix();
for (const _child of object.container.children) {
_child.matrixAutoUpdate = false;
_child.updateMatrix();
}
}
this.items.push(object);
return object;
}
}
Materials.js代码
import * as THREE from "three";
import FloorShadowMaterial from "../materials/FloorShadow.js";
import MatcapMaterial from "../materials/Matcap.js";
export default class Materials {
constructor(_options) {
this.resources = _options.resources;
this.items = {};
this.setShades();
this.setFloorShadow();
}
setShades() {
this.shades = {};
this.shades.items = {};
this.shades.indirectColor = "#d04500";
this.shades.uniforms = {
uRevealProgress: 0,
uIndirectDistanceAmplitude: 1.75,
uIndirectDistanceStrength: 0.5,
uIndirectDistancePower: 2.0,
uIndirectAngleStrength: 1.5,
uIndirectAngleOffset: 0.6,
uIndirectAnglePower: 1.0,
uIndirectColor: null,
};
// White
this.shades.items.white = new MatcapMaterial();
this.shades.items.white.name = "shadeWhite";
this.shades.items.white.uniforms.matcap.value =
this.resources.items.matcapWhiteTexture;
this.items.white = this.shades.items.white;
// Orange
this.shades.items.orange = new MatcapMaterial();
this.shades.items.orange.name = "shadeOrange";
this.shades.items.orange.uniforms.matcap.value =
this.resources.items.matcapOrangeTexture;
this.items.orange = this.shades.items.orange;
// Green
this.shades.items.green = new MatcapMaterial();
this.shades.items.green.name = "shadeGreen";
this.shades.items.green.uniforms.matcap.value =
this.resources.items.matcapGreenTexture;
this.items.green = this.shades.items.green;
// Brown
this.shades.items.brown = new MatcapMaterial();
this.shades.items.brown.name = "shadeBrown";
this.shades.items.brown.uniforms.matcap.value =
this.resources.items.matcapBrownTexture;
this.items.brown = this.shades.items.brown;
// Gray
this.shades.items.gray = new MatcapMaterial();
this.shades.items.gray.name = "shadeGray";
this.shades.items.gray.uniforms.matcap.value =
this.resources.items.matcapGrayTexture;
this.items.gray = this.shades.items.gray;
// Beige
this.shades.items.beige = new MatcapMaterial();
this.shades.items.beige.name = "shadeBeige";
this.shades.items.beige.uniforms.matcap.value =
this.resources.items.matcapBeigeTexture;
this.items.beige = this.shades.items.beige;
// Red
this.shades.items.red = new MatcapMaterial();
this.shades.items.red.name = "shadeRed";
this.shades.items.red.uniforms.matcap.value =
this.resources.items.matcapRedTexture;
this.items.red = this.shades.items.red;
// Black
this.shades.items.black = new MatcapMaterial();
this.shades.items.black.name = "shadeBlack";
this.shades.items.black.uniforms.matcap.value =
this.resources.items.matcapBlackTexture;
this.items.black = this.shades.items.black;
// Green emerald
this.shades.items.emeraldGreen = new MatcapMaterial();
this.shades.items.emeraldGreen.name = "shadeEmeraldGreen";
this.shades.items.emeraldGreen.uniforms.matcap.value =
this.resources.items.matcapEmeraldGreenTexture;
this.items.emeraldGreen = this.shades.items.emeraldGreen;
// Purple
this.shades.items.purple = new MatcapMaterial();
this.shades.items.purple.name = "shadePurple";
this.shades.items.purple.uniforms.matcap.value =
this.resources.items.matcapPurpleTexture;
this.items.purple = this.shades.items.purple;
// Blue
this.shades.items.blue = new MatcapMaterial();
this.shades.items.blue.name = "shadeBlue";
this.shades.items.blue.uniforms.matcap.value =
this.resources.items.matcapBlueTexture;
this.items.blue = this.shades.items.blue;
// Yellow
this.shades.items.yellow = new MatcapMaterial();
this.shades.items.yellow.name = "shadeYellow";
this.shades.items.yellow.uniforms.matcap.value =
this.resources.items.matcapYellowTexture;
this.items.yellow = this.shades.items.yellow;
// Metal
this.shades.items.metal = new MatcapMaterial();
this.shades.items.metal.name = "shadeMetal";
this.shades.items.metal.uniforms.matcap.value =
this.resources.items.matcapMetalTexture;
this.items.metal = this.shades.items.metal;
// 颜色处理
this.shades.updateMaterials = () => {
this.shades.uniforms.uIndirectColor = new THREE.Color(
this.shades.indirectColor
);
for (const _uniformName in this.shades.uniforms) {
const _uniformValue = this.shades.uniforms[_uniformName];
for (const _materialKey in this.shades.items) {
const material = this.shades.items[_materialKey];
material.uniforms[_uniformName].value = _uniformValue;
}
}
};
this.shades.updateMaterials();
}
//预留
setFloorShadow() {
this.items.floorShadow = new FloorShadowMaterial();
}
}
materials/FloorShadow.js代码
import * as THREE from "three";
import shaderFragment from "../../assets/shaders/floorShadow/fragment.glsl";
import shaderVertex from "../../assets/shaders/floorShadow/vertex.glsl";
export default function () {
const uniforms = {
tShadow: { value: null },
uShadowColor: { value: null },
uAlpha: { value: null },
};
const material = new THREE.ShaderMaterial({
wireframe: false,
transparent: true,
uniforms,
vertexShader: shaderVertex,
fragmentShader: shaderFragment,
});
return material;
}
materials/Matcap.js代码
import * as THREE from "three";
import shaderFragment from "../../assets/shaders/matcap/fragment.glsl";
import shaderVertex from "../../assets/shaders/matcap/vertex.glsl";
export default function () {
const uniforms = {
...THREE.UniformsLib.common,
...THREE.UniformsLib.bumpmap,
...THREE.UniformsLib.normalmap,
...THREE.UniformsLib.displacementmap,
...THREE.UniformsLib.fog,
matcap: { value: null },
uRevealProgress: { value: null },
uIndirectDistanceAmplitude: { value: null },
uIndirectDistanceStrength: { value: null },
uIndirectDistancePower: { value: null },
uIndirectAngleStrength: { value: null },
uIndirectAngleOffset: { value: null },
uIndirectAnglePower: { value: null },
uIndirectColor: { value: null },
};
const extensions = {
derivatives: false,
fragDepth: false,
drawBuffers: false,
shaderTextureLOD: false,
};
const defines = {
MATCAP: "",
};
const material = new THREE.ShaderMaterial({
wireframe: false,
transparent: false,
uniforms,
extensions,
defines,
lights: false,
vertexShader: shaderVertex,
fragmentShader: shaderFragment,
});
return material;
}
代码解读
index.js 中
新增了 start()在用户点击开始按钮时触发,
setMaterials() 对场景内基本材质做了统一处理,
setObjects() 是对模型的统一管理,
setSections()开始场景中各个模型加载。
Objects.js中对场景中模型进行了统一管理,包含添加 合并 对不同模型进行不同处理。
Materials.js中对场景模型进行了材质处理。
materials/FloorShadow.js与materials/Matcap.js分别是采用自定义的着色器材质。