エンジニアの副業は週1からでも可能?副業の例や探し方も解説
- ITエンジニア
- 副業
「波が広がるようにビルがあらわれるアニメーション」を、ゲームなどで目にしたことはありませんか?
今回はthree.jsとTweenMax(GSAP)を使って、波形アニメーションを作る方法をご紹介します。
OK this is better, using buildings instead of pois. #gamedev #indiedev #madewithunity #unity3d #builtwithmapbox pic.twitter.com/U0dI3Uqtym
— Baran Kahyaoglu (@brnkhy) October 19, 2017
今回はBaran Kahyaogluによる、こちらの作品を参考に制作しました。
波のようなアニメーションは、カメラとの距離に基づいて出現するビルのグリッドによって作られています。
遠いところにあるビルを霞ませることにより奥行きを表現している他、視覚的なランダムさを生み出すためにそれぞれのビルのサイズも変更しています。
まずはデモ用のマークアップを作成しましょう。
すべてのコードはcanvas要素内で実行されるため、とてもシンプルです。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="target" content="all">
<meta http-equiv="cleartype" content="on">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<title>Buildings Wave</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js">></script>
<script src="https://Threejs.org/examples/js//loaders/OBJLoader.js" ></script>
<script src="https://Threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
</head>
<body>
</body>
</html>
html, body {
margin: 0;
padding: 0;
background-color: #fff;
color: #fff;
box-sizing: border-box;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
}
メインクラス内にinitという関数を作成します。以降のメソッドはすべてこの中に追加しましょう。
init() {
this.group = new THREE.Object3D();
this.gridSize = 40;
this.buildings = [];
this.fogConfig = {
color: '#fff',
near: 1,
far: 138
};
}
createScene() {
this.scene = new THREE.Scene();
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(this.renderer.domElement);
// this is the line that will give us the nice foggy effect on the scene
this.scene.fog = new THREE.Fog(this.fogConfig.color, this.fogConfig.near, this.fogConfig.far);
}
シーンにカメラを追加しましょう。
createCamera() {
const width = window.innerWidth;
const height = window.innerHeight;
this.camera = new THREE.PerspectiveCamera(20, width / height, 1, 1000);
// set the distance our camera will have from the grid
// this will give us a nice frontal view with a little perspective
this.camera.position.set(3, 16, 111);
this.scene.add(this.camera);
}
次に、地面を作りましょう。
addFloor() {
const width = 200;
const height = 200;
const planeGeometry = new THREE.PlaneGeometry(width, height);
// all materials can be changed according to your taste and needs
const planeMaterial = new THREE.MeshStandardMaterial({
color: '#fff',
metalness: 0,
emissive: '#000000',
roughness: 0,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
planeGeometry.rotateX(- Math.PI / 2);
plane.position.y = 0;
this.scene.add(plane);
}
グリッドを構築する前に、3Dモデルを読み込んでおきます。
loadModels(path, onLoadComplete) {
const loader = new THREE.OBJLoader();
loader.load(path, onLoadComplete);
}
onLoadModelsComplete(model) {
// our buildings.obj file contains many models
// so we have to traverse them to do some initial setup
this.models = [...model.children].map((model) => {
// since we don't control how the model was exported
// we need to scale them down because they are very big
// scale model down
const scale = .01;
model.scale.set(scale, scale, scale);
// position it under the ground
model.position.set(0, -14, 0);
// allow them to emit and receive shadow
model.receiveShadow = true;
model.castShadow = true;
return model;
});
// our list of models are now setup
}
addAmbientLight() {
const ambientLight = new THREE.AmbientLight('#fff');
this.scene.add(ambientLight);
}
ここではじめて、ビルをグリッドレイアウトに配置します。
createGrid() {
// define general bounding box of the model
const boxSize = 3;
// define the min and max values we want to scale
const max = .009;
const min = .001;
const meshParams = {
color: '#fff',
metalness: .58,
emissive: '#000000',
roughness: .18,
};
// create our material outside the loop so it performs better
const material = new THREE.MeshPhysicalMaterial(meshParams);
for (let i = 0; i < this.gridSize; i++) {
for (let j = 0; j < this.gridSize; j++) {
// for every iteration we pull out a random model from our models list and clone it
const building = this.getRandomBuiding().clone();
building.material = material;
building.scale.y = Math.random() * (max - min + .01);
building.position.x = (i * boxSize);
building.position.z = (j * boxSize);
// add each model inside a group object so we can move them easily
this.group.add(building);
// store a reference inside a list so we can reuse it later on
this.buildings.push(building);
}
}
this.scene.add(this.group);
// center our group of models in the scene
this.group.position.set(-this.gridSize - 10, 1, -this.gridSize - 10);
}
完成度を高くするため、スポットライトも追加します。
addSpotLight() {
const light = { color: '#fff', x: 100, y: 150, z: 100 };
const spotLight = new THREE.SpotLight(light.color, 1);
spotLight.position.set(light.x, light.y, light.z);
spotLight.castShadow = true;
this.scene.add(spotLight);
}
さらにポイントライトも追加しましょう。
addPointLight(params) {
// sample params
// {
// color: '#00ff00',
// intensity: 4,
// position: {
// x: 18,
// y: 22,
// z: -9,
// }
// };
const pointLight = new THREE.PointLight(params.color, params.intensity);
pointLight.position.set(params.position.x, params.position.y, params.position.z);
this.scene.add(pointLight);
}
アニメーションをつける前に、カメラまでのz軸の距離に従ってビルを並べます。
sortBuildingsByDistance() {
this.buildings.sort((a, b) => {
if (a.position.z > b.position.z) {
return 1;
}
if (a.position.z < b.position.z) {
return -1;
}
return 0;
}).reverse();
}
ビルにアニメーションをつけましょう。リスト内の位置に基づいて、アニメーションの長さを定義します。
showBuildings() {
this.sortBuildingsByDistance();
this.buildings.map((building, index) => {
TweenMax.to(building.position, .3 + (index / 350), { y: 1, ease: Power3.easeOut, delay: index / 350 });
});
}
すると、このようなアニメーションが完成します。
今回はBack Studioのモデルを使用しましたが、この記事を参考にぜひオリジナルのモデルで波形アニメーションを楽しんでみてください。
(原文:Ion D. Filho 翻訳:codrops)
こちらもおすすめ!▼
チュートリアルをなぞるだけ!Three.jsでインタラクティブエフェクトを作ろう
Workship MAGAZINE