应用技术栈
webpack
threejs
typescript
第一步,准备工作(思考游戏玩法,绘制思维导图,搭建项目,创建场景)
文件目录
│ bash.exe.stackdump
│ package-lock.json
│ package.json
│ README.md
│ tsconfig.json
│ webpack.config.js
│ webpack.plugins.js
│
└─src
├─html
│ index.html // html
│
└─screen
index.ts // ts入口文件
下面是主要的功能以及场景元素的选择
来自灵魂画师绘制的构图
首先创建场景以及其他必要元素
一堆代码
const THREE = require("three");
import { OrbitControls } from "../../node_modules/three/examples/jsm/controls/OrbitControls";
class CreateScene {
// 屏幕宽度
width: number = window.innerWidth;
// 屏幕高度
height: number = window.innerHeight;
// 3d容器
container: any = document.body;
frustumSize = 2000;
scene // 场景
renderer // 渲染器
camera // 相机
controls // 控制器
constructor() {
this.createScene()
this.createCamera()
this.createRenderer()
this.createControls()
this.render()
this.axesHelper(100)
this.createBackground()
// 监听屏幕尺寸变化
window.addEventListener("resize", this.onWindowResized.bind(this), false);
}
// 创建场景
createScene(): void {
this.scene = new THREE.Scene();
}
// 创建渲染器
createRenderer(): void {
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(this.width, this.height);
document.body.appendChild(this.renderer.domElement);
}
// 创建相机
createCamera(): void {
this.camera = new THREE.OrthographicCamera(this.width / - 2, this.width / 2, this.height / 2, this.height / - 2, 1, this.frustumSize);
this.camera.zoom = 3
console.log(this.camera)
this.camera.position.set(200, 250, 200)
this.scene.add(this.camera);
this.camera.updateProjectionMatrix()
}
// 创建控制器
createControls(): void {
this.controls = new OrbitControls(
this.camera,
this.renderer.domElement
);
this.controls.screenSpacePanning = true
this.controls.target = new THREE.Vector3(0, 100, 0)
}
// 渲染动画
animate(): void {
requestAnimationFrame(this.render.bind(this));
}
// 渲染
render(): void {
this.animate()
this.renderer.render(this.scene, this.camera);
this.controls.update();
}
// 创建坐标轴辅助线
axesHelper(len: number): void {
const axesHelper = new THREE.AxesHelper(len);
this.scene.add(axesHelper);
}
// 监听屏幕改变
onWindowResized() {
this.camera.left = this.width / - 2;
this.camera.right = this.width / 2;
this.camera.top = this.height / 2;
this.camera.bottom = this.height / - 2;
this.camera.updateProjectionMatrix();
this.width = window.innerWidth
this.height = window.innerHeight
this.renderer.setSize(this.width, this.height);
}
// 创建天空背景
createBackground(): any {
const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 32;
const context = canvas.getContext('2d');
const gradient = context.createLinearGradient(0, 0, 0, 32);
gradient.addColorStop(0.0, '#014a84');
gradient.addColorStop(0.5, '#0561a0');
gradient.addColorStop(1.0, '#437ab6');
context.fillStyle = gradient;
context.fillRect(0, 0, 1, 32);
const sky = new THREE.Mesh(
new THREE.SphereBufferGeometry(1000),
new THREE.MeshBasicMaterial({ map: new THREE.CanvasTexture(canvas), side: THREE.BackSide })
);
this.scene.add(sky);
}
}
export { CreateScene, THREE }
然后在screen/index.ts
入口文件直接引用并实例化
import {CreateScene,THREE} from '../createScene/index'
const _this = new CreateScene()
创建后的场景如下
第二步 创建底座以及第一个可以移动的主角
创建主角
initFloor() {
const w: number = 30
const h: number = 50
const l: number = 30
const floorParams = {
w: w,
h: h,
l: l,
x: w / 2,
y: h / 2,
z: l / 2
}
this.floorCube = createCube(floorParams)
this.floorGroup.add(this.floorCube)
}
createCube
是封装的一个方法,主要功能创建方块,包括底板和主角
// 创建方块时候需要的参数
interface cubeParams {
w: number // 宽度 对应X轴
h: number // 高度 对应Y轴
l: number // 长度 对应Z轴
x: number // x轴位置
y: number // y轴位置
z: number // z轴位置
}
// 创建方块
export function createCube(p: cubeParams): any {
const geometry = new THREE.BoxGeometry(p.w, p.h, p.l);
const material = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh(geometry, material);
cube.position.set(p.x, p.y, p.z)
return cube
}
创建完成后
接下来需要创建主角(可移动方块)
需要获取的信息为底板的高度,底板的顶点信息 底板的位置信息,过往主角都将视为底板内容,
所以底板不直接添加到scene
场景内,而是创建一个group
可以通过封装好的getBox
方法获取size
封装好的getBox方法
const THREE = require("three");
function getBox(mesh: any) {
let b = new THREE.Box3();
b.expandByObject(mesh);
return b
}
// 获取尺寸
// 模型,vector3
export function getSize(mesh: any, v3:any) {
getBox(mesh).getSize(v3);
}
// 获取世界坐标
export function getPosition(mesh:any, v3: any) {
mesh.getWorldPosition(v3)
}
创建主角并设置位置信息
createlead() {
const size = new THREE.Vector3()
const mesh = this.floorGroup
// 获取尺寸
getSize(mesh, size)
const position = new THREE.Vector3()
// 获取底板的位置 默认应该都是0
getPosition(mesh, position)
const gy = position.y // 底板的Y值
const y = size.y + gy + this.leadY / 2 // 主角的Y值
// 设定第奇数个主角从z轴的负方向来,第偶数个主角从X轴方向来
// 需要一个主角计数器,同样可以用来计算分数
// 起始点距离底板30
// 主角初始位置
const flag = this.leadCount % 2 === 0 // 是否是偶数主角
// x 起始点
let sx = (flag ? -this.startPoint : 0) + this.size / 2
// z 起始点
let sz = (flag ? 0 : -this.startPoint) + this.size / 2
// 创建一个主角
const leadParam = {
w: this.size,
h: this.leadY,
l: this.size,
x: sx,
y: y,
z: sz
}
const leadCube = createCube(leadParam)
this.scene.add(leadCube)
// 创建角色后计数器自增1
this.leadCount++
}
多调用几次便可以从两侧创建主角
下面是initGame代码 又是一堆代码 汗!!!
const THREE = require("three");
import { createCube } from '../utils/tools'
import { getSize, getPosition } from '../utils/getBox'
class CreateGame {
scene: any
floorCube: any // 初始底板
floorGroup: any // 底板组
size: number = 30 // 主角宽度和长度
leadY: number = 5 // 主角高度
leadCount: number = 0 // 计数器
startPoint: number = 60 // 主角起始位置 x或z
leadInterval: any = null // 循环
constructor(element: any) {
this.scene = element.scene
this.floorGroup = new THREE.Group()
this.scene.add(this.floorGroup)
this.initFloor()
}
initFloor() {
const w: number = this.size
const h: number = 50
const l: number = this.size
const floorParams = {
w: w,
h: h,
l: l,
x: w / 2,
y: h / 2,
z: l / 2
}
this.floorCube = createCube(floorParams)
this.floorGroup.add(this.floorCube)
this.floorGroup.updateMatrix()
}
createlead() {
const size = new THREE.Vector3()
const mesh = this.floorGroup
// 获取尺寸
getSize(mesh, size)
const position = new THREE.Vector3()
// 获取底板的位置 默认应该都是0
getPosition(mesh, position)
const gy = position.y // 底板的Y值
const y = size.y + gy + this.leadY / 2 // 主角的Y值
// 设定第奇数个主角从z轴的负方向来,第偶数个主角从X轴方向来
// 需要一个主角计数器,同样可以用来计算分数
// 起始点距离底板30
// 主角初始位置
const flag:boolean = this.leadCount % 2 === 0 // 是否是偶数主角
// x 起始点
let sx:number = (flag ? -this.startPoint : 0) + this.size / 2
// z 起始点
let sz:number = (flag ? 0 : -this.startPoint) + this.size / 2
// 创建一个主角
const leadParam = {
w: this.size,
h: this.leadY,
l: this.size,
x: sx,
y: y,
z: sz
}
const leadCube = createCube(leadParam)
this.floorGroup.add(leadCube)
// 创建角色后计数器自增1
this.leadCount++
}
}
export { CreateGame }
游戏共分为4个步骤来写文章
第一步 基础 初始化游戏
第二步 控制 控制主角移动以及停止