这是我参与更文挑战的第6天,活动详情查看: 更文挑战
上一章介绍了Oasis里的坐标系,本篇文章就介绍如何将一个2D游戏的场景布置出来
精灵资源加载
Sprite 是 2D 图形对象,用于角色、道具、子弹以及一些其他 2D 游戏要素。这些图形是从 Texture2D 获得的。Sprite 类主要标识应用于特定Sprite的图像部分。然后 Entity 上的 SpriteRenderer 组件可以使用此信息来实际显示图形。
我们先加载一个素材看看效果
// 加载纹理资源
const groundTexture: Texture2D = await engine.resourceManager.load(textureList[0])
// 创建精灵实体
const groundEntity = rootEntity.createChild('groundSprite')
const groundRenderer = groundEntity.addComponent(SpriteRenderer)
const groundsprite = new Sprite(engine, groundTexture)
groundRenderer.sprite = groundsprite
// 设置实体坐标
const groundScreenpoint = new Vector3(0, 0)
const groundWorldpoint = new Vector3()
cameramash.screenToWorldPoint(groundScreenpoint, groundWorldpoint)
groundEntity.transform.position = groundWorldpoint
复制代码
设置模型坐标原点
如果仔细观察的话可以发现在屏幕的左上角有1/4个方块,为方便观察我们使用
groundEntity.transform.scale = new Vector3(5, 5)
将素材放大5倍。
我们将实体的位置设置在屏幕(0, 0)上,而Oasis里默认的模型坐标原点在中心点上,所以此时只能看到1/4个方块。如果我们想要看到整个素材就需要设置坐标为
(groundEntity.width / 2, groundEntity.height / 2)。而我们可以使用 sprite.pivot 来重新设置模型坐标的原点。
详情介绍可以查看官网的精灵资源(注:rect方法在V0.4版本中为region)
我们这里将左上角设置为原点,便可以看到一个完整的方块
groundsprite.pivot = new Vector2(0, 1)
素材分辨率设置
我这里的引用素材大小为48*48
然而,渲染后的内容明显小于素材实际大小
这是因为在Sprite下面有一个pixelsPerUnit参数,我们先来看一下官网对这个参数的介绍
The number of pixels in the sprite that correspond to one unit in world space.(对应世界空间中一个单位的精灵中的像素数。)
我对这个介绍的理解是:
在世界坐标下,一个单位的长度对应多少屏幕像素点。
使用以下代码可以求出一个单位的长度对应多少屏幕像素点
const worldPoint = new Vector3(0, 0, 0)
const screenOut = new Vector4()
cameramash.worldToScreenPoint(worldPoint, screenOut)
const worldPoint1 = new Vector3(1, 0, 0)
const screenOut1 = new Vector4()
cameramash.worldToScreenPoint(worldPoint1, screenOut1)
console.log(`pixelsPerUnit:${screenOut1.x - screenOut.x}`) // 128
复制代码
我们可以使用console.log(Sprite.pixelsPerUnit)来查看是否也为128
console.log(groundsprite.pixelsPerUnit) // 128
复制代码
我们将pixelsPerUnit设置为10 可以看到素材相较与pixelsPerUnit为128时大了很多
groundsprite.pixelsPerUnit = 10
复制代码
所以,可以得出一个简单的结论:在世界坐标下,如果一个单位的长度对应更多的屏幕像素则图片会变得更小;反之更大。即,
pixelsPerUnit 数字越大,图片越小。
所以就可以将素材通过pixelsPerUnit调整到合适的大小,这里将pixelsPerUnit调整为50
groundsprite.pixelsPerUnit = 50
自适应缩放
在游戏场景中,素材的大小应该可以随着用户屏幕的宽高自适应调整,所以精灵的宽高需要通过屏幕计算而来。
这里渲染了两排,每排25个精灵
// 获取canvas的宽
const canvasX = engine.canvas.width
// 确定需要25个精灵后,用Canvas的宽除以每个精灵的宽再除以25得到每个精灵需要缩放的倍数
const scaleTimes = canvasX / 25 / groundTexture.width
// 最后通过transform.scale设置实体的变换
groundEntity.transform.scale = new Vector3(scaleTimes, scaleTimes)
复制代码
这样无论用户显示器的宽度为多少,都能够按比例显示大小,再通过两个while循环渲染出来
两排地面完整代码
// 获取精灵材质
const groundTexture: Texture2D = await engine.resourceManager.load(textureList[0])
// 设置初始行列变量
let i = 0
let j = 0
// 计算缩放比例
const scaleTimes = canvasX / 25 / groundTexture.width
// 双循环渲染
while (j < 2) {
while (i < 25) {
// 创建精灵实体
const groundEntity = rootEntity.createChild(`groundSprite_${i}`)
const groundRenderer = groundEntity.addComponent(SpriteRenderer)
const groundsprite = new Sprite(engine, groundTexture)
// 设置模型中心为左上角
groundsprite.pivot = new Vector2(0, 1)
// 设置pixelsPerUnit值为50
groundsprite.pixelsPerUnit = 50
groundRenderer.sprite = groundsprite
// 计算实体屏幕坐标系X,Y轴位置
const pointX = groundTexture.width * i * scaleTimes
const pointY = canvasY - groundTexture.height * scaleTimes * (j + 1)
// 转换为世界坐标
const screenpoint = new Vector3(pointX, pointY)
const out = new Vector3()
cameramash.screenToWorldPoint(screenpoint, out)
// 设置位置和缩放
groundEntity.transform.position = out
groundEntity.transform.scale = new Vector3(scaleTimes, scaleTimes)
i++
}
i = 0
j++
}
复制代码
再通过同样的方法添加3朵云和一个角色,设置一下背景颜色
一个基本的场景布置就完成了
完整代码
import {
Camera,
WebGLEngine,
Vector2,
Vector3,
Texture2D,
SpriteRenderer,
Sprite,
AssetType,
BackgroundMode
} from 'oasis-engine'
export async function createOasis (): Promise<void> {
// 初始化Engine
const engine = new WebGLEngine('canvas')
engine.canvas.resizeByClientSize()
// 获取场景根实体
const scene = engine.sceneManager.activeScene
const rootEntity = scene.createRootEntity('root')
scene.background.mode = BackgroundMode.SolidColor
scene.background.solidColor.setValue(0.76, 0.76, 0.74, 1)
// 获取Canvas宽高
const canvasX = engine.canvas.width
const canvasY = engine.canvas.height
// 创建一个相机实体
const cameraEntity = rootEntity.createChild('camera_entity')
const cameramash = cameraEntity.addComponent(Camera)
cameraEntity.transform.position = new Vector3(0, 0, 20)
cameramash.isOrthographic = true
// 创建资源
const textureList = [
{
url: require('../assets/ground.png'),
type: AssetType.Texture2D
}, {
url: require('../assets/cloud.png'),
type: AssetType.Texture2D
}, {
url: require('../assets/i1.png'),
type: AssetType.Texture2D
}
]
// 创建地面实体
const groundTexture: Texture2D = await engine.resourceManager.load(textureList[0])
let i = 0
let j = 0
const scaleTimes = canvasX / 25 / groundTexture.width
while (j < 2) {
while (i < 25) {
const groundEntity = rootEntity.createChild(`groundSprite_${i}`)
const groundRenderer = groundEntity.addComponent(SpriteRenderer)
const groundsprite = new Sprite(engine, groundTexture)
groundsprite.pivot = new Vector2(0, 1)
groundsprite.pixelsPerUnit = 50
groundRenderer.sprite = groundsprite
const pointX = groundTexture.width * i * scaleTimes
const pointY = canvasY - groundTexture.height * scaleTimes * (j + 1)
const screenpoint = new Vector3(pointX, pointY)
const out = new Vector3()
cameramash.screenToWorldPoint(screenpoint, out)
groundEntity.transform.position = out
groundEntity.transform.scale = new Vector3(scaleTimes, scaleTimes)
i++
}
i = 0
j++
}
// 创建角色实体
const roleTexture: Texture2D = await engine.resourceManager.load(textureList[2])
const roleEntity = rootEntity.createChild('roleSprite')
const roleRenderer = roleEntity.addComponent(SpriteRenderer)
const rolesprite = new Sprite(engine, roleTexture)
rolesprite.pivot = new Vector2(0, 1)
rolesprite.pixelsPerUnit = 50
roleRenderer.sprite = rolesprite
const roleX = groundTexture.height * scaleTimes * 5
const roleY = canvasY - groundTexture.height * scaleTimes * 2 - roleTexture.height * scaleTimes
const rolepoint = new Vector3(roleX, roleY)
const roleout = new Vector3()
cameramash.screenToWorldPoint(rolepoint, roleout)
roleEntity.transform.position = roleout
roleEntity.transform.scale = new Vector3(scaleTimes, scaleTimes)
// 创建云实体
const cloud1Texture: Texture2D = await engine.resourceManager.load(textureList[1])
let cloudPlace = 0
while (cloudPlace < 3) {
const cloud1Entity = rootEntity.createChild(`cloud1Sprite_${cloudPlace}`)
const cloud1Renderer = cloud1Entity.addComponent(SpriteRenderer)
const cloud1sprite = new Sprite(engine, cloud1Texture)
cloud1sprite.pivot = new Vector2(0, 1)
cloud1sprite.pixelsPerUnit = 50
cloud1Renderer.sprite = cloud1sprite
const cloud1ponintX = canvasX * cloudPlace / 3 + cloud1Texture.width
const cloud1screenpoint = new Vector3(cloud1ponintX, 0)
const cloud1out = new Vector3()
cameramash.screenToWorldPoint(cloud1screenpoint, cloud1out)
cloud1Entity.transform.position = cloud1out
cloudPlace++
}
// 启动引擎
engine.run()
}
复制代码
end
如果可以的话,可以点个小小的赞
respect by myself