更新履歴
2017年11月20日ソースコードへの参照を追加
swift + metalで3D描画する
はじめに
swift + metalで3Dアクションゲームを作りました。
「メガネニテンセイ」略して「メガテン」という、メガネを操作して戦うゲーム作りました!
— 藤城@眼鏡転生公開 (@fuziki333) 2017年11月15日
無料で容量も20MBくらいなんで、一度遊んでみてください!
お願いします\(´・∀・` )/https://t.co/Re6VZHj4lZ pic.twitter.com/4Tycuy6p3t
最初に描画するまでが大変だったので、objファイルを読み込んで描画するまでまとめて公開してみました。
これからmetalを使ってみようと考えている方の参考になれば嬉しいです。
ソースコードはこちら↓
https://github.com/fuziki/metalTest
MetalEzとWord
主にMetalEzがMetalのAPIの呼び出し、Worldがカメラや戦闘機の管理をしています。
MetalEzクラス
MetalEzは描画開始前にMetalEzClassDelegatのupdateを呼び出します。
次に、pipelinestateを順番にセットして、MetalEzClassDelegatのdrawを呼び出します。
どのパイプラインが設定されているかは、RendererTypeによって識別します。
ViewController内でオブジェクトを生成し、MetalEzClassDelegateを自身が受け取るように設定します。
mtlEz = MetalEz()
mtlEz.delegate = self
mtlEz.setupMetal(mtkView: self.view as! MTKView!)
protocol MetalEzClassDelegate {
func update()
func draw(type: MetalEzRenderingEngine.RendererType)
}
公開したコードには、3Dオブジェクトを描画するパイプラインが2種類存在します。
1つは影あり(左の立方体)、もう1つは影なし(右の戦闘機)です。
それぞれ、RendererTypeはmeshとmesh_nonlightingです。
enum RendererType: Int {
case mesh
case mesh_add
case mesh_nonlighting
}
Worldクラス
描画するオブジェクトや、カメラの管理をします。
Cameraクラス
カメラの位置と方向、上の向きを設定します。
MetalEzクラスのlookAtメソッドを用いて設定します。
class Camera {
weak var world: World!
private var rot: Float = 0
private var sgn: Float = 0.01
init(world myworld: World) {
world = myworld
}
func update() {
rot += sgn
if abs(rot) > 0.8 { sgn *= -1 }
world.mtlEz.lookAt(from: float3(0, 1, -1), direction: normalize(float3(sin(rot), -0.5, 1)), up: float3(0, 1, 0))
}
}
MeshDrawer
戦闘機と立方体はMeshDrawerを用いて描画しています。
updateが呼び出された時に、モデル変換行列をセットします。
drawメソッドを実行すると描画されます。
extension MetalEz {
class MeshDrawer {
private var mesh: MTKMesh
private var texture: MTLTexture
private var frameUniformBuffer: MTLBuffer
private weak var mtlEz: MetalEz!
var hidden: Bool = false
init(mtlEz m: MetalEz ,mesh v: MTKMesh, texture t: MTLTexture)
func set(modelMatrix: matrix_float4x4)
func draw()
}
}
class Realship {
weak var world: World!
private var drawer: MetalEz.MeshDrawer
private var rot: Float = 0.0
init(world myworld: World) {
world = myworld
let tex = world.mtlEz.loader.loadTexture(name: "shipDiffuse", type: "png")
let mesh = world.mtlEz.loader.loadMesh(name: "realship")
drawer = MetalEz.MeshDrawer(mtlEz: world.mtlEz ,mesh: mesh, texture: tex)
}
func update() {
rot += 1
var mat = matrix_identity_float4x4
mat = matrix_multiply(mat, Utils.translation(float3(2, -1, 3)))
mat = matrix_multiply(mat, Utils.rotation_y(radians: toRad(fromDeg: rot)))
drawer.set(modelMatrix: mat)
}
func draw(type: MetalEzRenderingEngine.RendererType) {
if type == .mesh_nonlighting {
drawer.draw()
}
}
}
まとめ
単純にAPIを呼び出しているだけなので、あまり書くことがありませんでした(;´・ω・)
ダメダメなコードですが、どなたかの役に立ったと信じてます(笑)
実際にリリースしたゲームでも同じ仕組みを使っています。
「メガネニテンセイ」というメガネに転生して戦うアクションゲームです。
無料で容量も20MB程度なので、swift + metalでゲームを作るとどんな感じになるのか、気になる場合は、一度ダウンロードしてみてくださいm(_ _)m
すぐに消していいのでお願いします!←
https://itunes.apple.com/jp/app/%E3%83%A1%E3%82%AC%E3%83%8D%E3%83%8B%E3%83%86%E3%83%B3%E3%82%BB%E3%82%A4/id1312709625?mt=8