一、创建底部自转的法阵
单纯创建个圆再把贴图加上就行,贴图如下,下载后记得修改贴图名字
this.circleTexturePath = 'assets/images/magic.png'
this.circleTexture = this.textureLoader.load(this.circleTexturePath)
let circleGeo = new CircleBufferGeometry(1, 32)
let circleMat = new MeshBasicMaterial({
map: this.circleTexture,
transparent: true,
side: DoubleSide,
depthWrite: false
})
let circle = new Mesh(circleGeo, circleMat)
circle.rotateX(-Math.PI / 2)
this.circles.push(circle)
teleporter.add(circle)
然后让它转起来,下面这个函数需要每帧更新
/**
* 更新传送阵底部的圆
*/
updateCircles() {
for (var i = 0; i < this.circles.length; i++) {
this.circles[i].rotateZ(0.02)
}
}
二、添加一圈立体的光晕
这个也简单,创建个没有顶部和底部的圆柱,附上贴图就行,贴图如下
this.aroundTexturePath = 'assets/images/guangyun.png'
this.aroundTexture = this.textureLoader.load(this.aroundTexturePath)
//getCylinderGeo函数是自己写的一个方法,具体代码可看下面的完整代码部分
let aroundGeo = this.getCylinderGeo(0.5, 2)
let aroundMat = new MeshBasicMaterial({
map: this.aroundTexture,
transparent: true,
side: DoubleSide,
wireframe: false,
depthWrite: false
})
let around = new Mesh(aroundGeo, aroundMat)
this.arounds.push(around)
teleporter.add(around)
下图为不加贴图并显示线框的圆柱体
下图为正常显示的加了贴图的效果
然后让这个圆柱也旋转起来,下面这个函数也需要每帧更新
/**
* 更新传送阵四周的光壁
*/
updateArounds() {
for (var i = 0; i < this.arounds.length; i++) {
this.arounds[i].rotateY(0.01)
}
}
此时效果如下
看上面的gif,有那么点感觉了哈,不过还不够酷炫,那就再加个光柱,反方向旋转,分别修改上面的代码如下
this.aroundTexturePath = 'assets/images/guangyun.png'
this.aroundTexture = this.textureLoader.load(this.aroundTexturePath)
//getCylinderGeo函数是自己写的一个方法,具体代码可看下面的完整代码部分
let aroundGeo = this.getCylinderGeo(0.5, 2)
let aroundMat = new MeshBasicMaterial({
map: this.aroundTexture,
transparent: true,
side: DoubleSide,
wireframe: false,
depthWrite: false
})
let around = new Mesh(aroundGeo, aroundMat)
this.arounds.push(around)
teleporter.add(around)
//复制出来第二个光柱
let around2 = around.clone()
around2.userData.aroundScaleOffset = 0.01 //下一步使用
teleporter.add(around2)
this.arounds2.push(around2)
更新函数修改为如下
/**
* 更新传送阵四周的光壁
*/
updateArounds() {
for (var i = 0; i < this.arounds.length; i++) {
this.arounds[i].rotateY(0.01)
}
for (var i = 0; i < this.arounds2.length; i++) {
this.arounds2[i].rotateY(-0.01)
}
}
还可以再进一步修改下缩放,将更新函数修改为如下
/**
* 更新传送阵四周的光壁
*/
updateArounds() {
for (var i = 0; i < this.arounds.length; i++) {
this.arounds[i].rotateY(0.01)
}
for (var i = 0; i < this.arounds2.length; i++) {
this.arounds2[i].rotateY(-0.01)
if (this.arounds2[i].scale.x < 0.9 || this.arounds2[i].scale.x > 1.4) {
this.arounds2[i].userData.aroundScaleOffset *= -1
}
this.arounds2[i].scale.x -= this.arounds2[i].userData.aroundScaleOffset
this.arounds2[i].scale.z -= this.arounds2[i].userData.aroundScaleOffset
}
}
三、添加粒子效果
不加粒子效果总感觉不够酷炫,所以再加少许粒子,下面几张图是用PS制作的,有点粗糙了。。
代码如下:
createTeleporter() {
....
//创建 10 * 4 个粒子
for (var j = 0; j < 10; j++) {
for (var i = 0; i < this.pointTexture.length; i++) {
let sprite = this.getPoints(this.params.pointRangeRadius, this.params.height, this.pointTexture[i])
this.particles.push(sprite)
teleporter.add(sprite)
}
}
}
/**
* 获取点云效果 为了能控制每一个粒子的大小和位置,这里的每一个Points对象只包含一个点,要是粒子放太多了可能有性能问题
* @param {*} radius
* @param {*} height
* @param {*} texturePath
* @returns
*/
getPoints(radius, height, texture) {
const geometry = new BufferGeometry()
const vertices = [0, 0, 0]
geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3))
const material = new PointsMaterial({
size: Math.random() * (0.15 - 0.04) + 0.04,
map: texture,
blending: AdditiveBlending,
depthTest: false,
transparent: true,
opacity: 0.2 + Math.random() * 0.8
})
const particle = new Points(geometry, material)
//粒子上升速度
particle.userData.floatSpeed = 0.001 + Math.random() * 0.01
particle.userData.radius = radius
particle.position.x = Math.random() * radius * 2 - radius
particle.position.y = Math.random() * height
particle.position.z = Math.random() * radius * 2 - radius
return particle
}
再让这些粒子按不同速度上升,到一定高度时又回到最低点,循环往复,更新函数如下
/**
* 更新光点效果
*/
updatePatical() {
for (var i = 0; i < this.particles.length; i++) {
this.particles[i].position.y += this.particles[i].userData.floatSpeed
if (this.particles[i].position.y >= 2) {
//更新位置,y=0,x,z随机
this.particles[i].position.y = 0
this.particles[i].position.x =
Math.random() * this.particles[i].userData.radius * 2 - this.particles[i].userData.radius
this.particles[i].position.z =
Math.random() * this.particles[i].userData.radius * 2 - this.particles[i].userData.radius
//随机上升速度
this.particles[i].userData.floatSpeed = 0.001 + Math.random() * this.params.pointFloatSpeed
}
}
}
四、完整代码
贴图资源也可从这获取
1、引入库 import {TeleporterManager } from “./TeleporterManager.js”
2、创建实例 let teleporterManager = new TeleporterManager(this.scene)
3、创建传送阵 teleporterManager.createTeleporter()
4、在 requestAnimationFrame 更新的函数里调用传送阵更新函数 teleporterManager.update()
import {
CircleBufferGeometry,
TextureLoader,
Mesh,
MeshBasicMaterial,
DoubleSide,
BufferAttribute,
BufferGeometry,
Float32BufferAttribute,
PointsMaterial,
AdditiveBlending,
Points,
Object3D,
Vector3
} from 'three'
export class TeleporterManager {
constructor(scene) {
this.init(scene)
}
init(scene) {
this.scene = scene
this.params = {
segment: 32,
circleRadius: 1,
circleRotateSpeed: 0.02,
aroundRadius: 0.5,
aroundRotateSpeed: 0.01,
aroundScaleOffset: 0.01,
height: 2,
pointRangeRadius: 0.8,
pointMinSize: 0.04,
pointMaxSize: 0.15,
pointFloatSpeed: 0.01,
pointTexturePath: [
'assets/images/point1.png',
'assets/images/point2.png',
'assets/images/point3.png',
'assets/images/point4.png'
]
}
this.textureLoader = new TextureLoader()
this.circleTexturePath = 'assets/images/magic.png'
this.aroundTexturePath = 'assets/images/guangyun.png'
this.textureLoader.load(this.circleTexturePath, texture => {
this.circleTexture = texture
})
this.textureLoader.load(this.aroundTexturePath, texture => {
this.aroundTexture = texture
})
this.pointTexture = []
for (var i = 0; i < this.params.pointTexturePath.length; i++) {
this.textureLoader.load(this.params.pointTexturePath[i], texture => {
this.pointTexture.push(texture)
})
}
this.circles = []
this.arounds = []
this.arounds2 = []
this.particles = []
this.teleporters = []
}
update() {
this.updateCircles()
this.updateArounds()
this.updatePatical()
}
clear() {
for (var i = 0; i < this.teleporters.length; i++) {
this.clearModel(this.teleporters[i])
}
this.scene = null
this.teleporters = []
this.circles = []
this.arounds = []
this.arounds2 = []
this.particles = []
this.textureLoader = null
this.circleTexture = null
this.aroundTexture = null
this.pointTexture = []
}
clearModel(model) {
if (!model) return
// dispose geometry
model.traverse(node => {
if (!node.isMesh) return
node.geometry.dispose()
})
}
/**
* 创建一个传送阵
*/
createTeleporter() {
let teleporter = new Object3D()
teleporter._type = 'TeleporterHelper'
this.scene.add(teleporter)
this.teleporters.push(teleporter)
let circleGeo = new CircleBufferGeometry(this.params.circleRadius, this.params.segment)
let circleMat = new MeshBasicMaterial({
map: this.circleTexture,
transparent: true,
side: DoubleSide,
depthWrite: false
})
let circle = new Mesh(circleGeo, circleMat)
circle.rotateX(-Math.PI / 2)
this.circles.push(circle)
teleporter.add(circle)
let aroundGeo = this.getCylinderGeo(this.params.aroundRadius, this.params.height)
let aroundMat = new MeshBasicMaterial({
map: this.aroundTexture,
transparent: true,
side: DoubleSide,
wireframe: false,
depthWrite: false
})
let around = new Mesh(aroundGeo, aroundMat)
this.arounds.push(around)
teleporter.add(around)
let around2 = around.clone()
around2.userData.aroundScaleOffset = this.params.aroundScaleOffset
around2.userData._type = around2._type
teleporter.add(around2)
this.arounds2.push(around2)
for (var j = 0; j < 10; j++) {
for (var i = 0; i < this.pointTexture.length; i++) {
let sprite = this.getPoints(this.params.pointRangeRadius, this.params.height, this.pointTexture[i])
this.particles.push(sprite)
teleporter.add(sprite)
}
}
}
/**
* 获取点云效果
* @param {*} radius
* @param {*} height
* @param {*} texturePath
* @returns
*/
getPoints(radius, height, texture) {
const geometry = new BufferGeometry()
const vertices = [0, 0, 0]
geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3))
const material = new PointsMaterial({
size: Math.random() * (this.params.pointMaxSize - this.params.pointMinSize) + this.params.pointMinSize,
map: texture,
blending: AdditiveBlending,
depthTest: false,
transparent: true,
opacity: 0.2 + Math.random() * 0.8
})
const particle = new Points(geometry, material)
particle.userData.floatSpeed = 0.001 + Math.random() * this.params.pointFloatSpeed
particle.userData.radius = radius
particle.position.x = Math.random() * radius * 2 - radius
particle.position.y = Math.random() * height
particle.position.z = Math.random() * radius * 2 - radius
return particle
}
/**
* 获取圆柱几何体
* @param {*} radius 半径
* @param {*} height 高度
* @param {*} segment 分段数
* @returns
*/
getCylinderGeo(radius = 1, height = 1, segment = 32) {
let bottomPos = []
let topPos = []
let bottomUvs = []
let topUvs = []
let angleOffset = (Math.PI * 2) / segment
let uvOffset = 1 / (segment - 1)
for (var i = 0; i < segment; i++) {
let x = Math.cos(angleOffset * i) * radius
let z = Math.sin(angleOffset * i) * radius
bottomPos.push(x, 0, z)
bottomUvs.push(i * uvOffset, 0)
topPos.push(x, height, z)
topUvs.push(i * uvOffset, 1)
}
bottomPos = bottomPos.concat(topPos)
bottomUvs = bottomUvs.concat(topUvs)
let face = []
for (var i = 0; i < segment; i++) {
if (i != segment - 1) {
face.push(i + segment + 1, i, i + segment)
face.push(i, i + segment + 1, i + 1)
} else {
face.push(segment, i, i + segment)
face.push(i, segment, 0)
}
}
let geo = new BufferGeometry()
geo.setAttribute('position', new BufferAttribute(new Float32Array(bottomPos), 3))
geo.setAttribute('uv', new BufferAttribute(new Float32Array(bottomUvs), 2))
geo.setIndex(new BufferAttribute(new Uint16Array(face), 1))
return geo
}
/**
* 更新传送阵底部的圆
*/
updateCircles() {
for (var i = 0; i < this.circles.length; i++) {
this.circles[i].rotateZ(this.params.circleRotateSpeed)
}
}
/**
* 更新传送阵四周的光壁
*/
updateArounds() {
for (var i = 0; i < this.arounds.length; i++) {
this.arounds[i].rotateY(this.params.aroundRotateSpeed)
}
for (var i = 0; i < this.arounds2.length; i++) {
this.arounds2[i].rotateY(-this.params.aroundRotateSpeed)
if (this.arounds2[i].scale.x < 0.9 || this.arounds2[i].scale.x > 1.4) {
this.arounds2[i].userData.aroundScaleOffset *= -1
}
this.arounds2[i].scale.x -= this.arounds2[i].userData.aroundScaleOffset
this.arounds2[i].scale.z -= this.arounds2[i].userData.aroundScaleOffset
}
}
/**
* 更新光点效果
*/
updatePatical() {
for (var i = 0; i < this.particles.length; i++) {
this.particles[i].position.y += this.particles[i].userData.floatSpeed
if (this.particles[i].position.y >= this.params.height) {
//更新位置,y=0,x,z随机
this.particles[i].position.y = 0
this.particles[i].position.x =
Math.random() * this.particles[i].userData.radius * 2 - this.particles[i].userData.radius
this.particles[i].position.z =
Math.random() * this.particles[i].userData.radius * 2 - this.particles[i].userData.radius
//随机上升速度
this.particles[i].userData.floatSpeed = 0.001 + Math.random() * this.params.pointFloatSpeed
}
}
}