はじめに
最近Godotというゲームエンジンを知りました
公式サイトhttps://godotengine.org
調べてみるとNimで書けるそうなので、書いてみます
Nimで書く
Godotではゲーム内に登場する要素をノード(Node)と表現します
このノードに動作をつけるために必要なのがスクリプトで、
- GDScript
- ビジュアルスクリプト
- C#
- C++
のいずれかの言語で書きます
ただし、非公式ではありますがこれ以外の言語でもスクリプトを書くことが可能で
その1つにNimがあります
こちらのライブラリを使ってhttps://github.com/pragmagic/godot-nim
Godot公式サイトにあるFPSチュートリアルのパート1をNimで書いてみます
使用したver.は以下のとおりです
- Nim: ver.1.2.2
- Godot: ver.3.2
書くときに注意すること
コードの読み替え
チュートリアルのコードはGDScriptなので、そのまま写しても動きません
いい感じに読み替える必要があります
とはいえ、そこまで難しくはなく
基本的にスネークケースからキャメルケースに読み替えればokです
例えばキーボードの入力を検知する関数のis_action_pressedはisActionPressedになります
GDScript | Nim |
---|---|
is_action_pressed | isActionPressed |
読み替えできないもの
単純な読み替えで対応できないものもあります
例えばチュートリアルにあるCameraノードを取得するときは、
GDScriptが**$Rotation_Helper/Cameraなのに対して
Nimはself.getNode("Rotation_Helper/Camera") as Camera**になります
(getNodeで取得すると、どんなモノもNodeとして扱われるのでas XXXでcastする必要があります)
この辺はライブラリのドキュメントに載っているサンプルを参考にしました
あと、標準ライブラリを使うこともあり
GDScriptのdeg2radは標準ライブラリmathのdegToRadを使いました
できたもの
import godot, math
import godotapi / [ kinematic_body, input_event_mouse_motion]
import godotapi/input as Input
const
GRAVITY = -24.8
MAX_SPEED = 20
JUMP_SPEED = 18
ACCEL = 4.5
DEACCEL = 16
MAX_SPRINT_SPEED = 30
SPRINT_ACCEL = 18
gdobj Player of KinematicBody:
var camera: Spatial
var rotationHelper: Spatial
var dir, vel = vec3()
var mouseSensitivity = 0.05
var isSprinting = false
var flashlight: SpotLight
method ready*() =
self.camera = self.getNode("Rotation_Helper/Camera") as Camera
self.rotationHelper = self.getNode("Rotation_Helper") as Spatial
self.flashlight = self.getNode("Rotation_Helper/Flashlight") as SpotLight
Input.setMouseMode(Input.MOUSE_MODE_CAPTURED)
method physicsProcess*(delta: float) =
self.processInput(delta)
self.processMovement(delta)
proc processInput(delta: float) =
self.dir = vec3()
let inputMovementVector = self.inputWASD().normalized()
let camXform = self.camera.globalTransform()
let vecZ = camXform.basis[2].normalized() * inputMovementVector.y
let vecX = camXform.basis[0].normalized() * inputMovementVector.x
let relativeDir = vecZ + vecX
self.dir.x = relativeDir.x
self.dir.z = -relativeDir.z
self.isSprinting = Input.isActionPressed("movement_sprint")
if Input.isActionJustPressed("flashlight"):
self.flash()
if Input.isActionJustPressed("ui_cancel"):
self.uiCancel()
if self.isOnFloor() and Input.isActionJustPressed("movement_jump"):
self.vel.y = JUMP_SPEED
proc processMovement(delta: float) =
self.dir.y = 0
self.dir = self.dir.normalized()
self.vel.y += delta * GRAVITY
var hvel = self.vel
hvel.y = 0
let target = if self.isSprinting:
self.dir * MAX_SPRINT_SPEED
else:
self.dir * MAX_SPEED
let accel = self.getAccel(hvel)
hvel = hvel.lerp(target, accel * delta)
self.vel.x = hvel.x
self.vel.z = hvel.z
self.vel = self.moveAndSlide(self.vel, vec3(0, 1, 0))
method input(event: InputEvent) =
if event of InputEventMouseMotion and Input.getMouseMode() == Input.MOUSE_MODE_CAPTURED:
let ev = event as InputEventMouseMotion
let rotateX = degToRad(ev.relative.y * self.mouseSensitivity)
let rotateY = degToRad(ev.relative.x * self.mouseSensitivity * -1)
self.rotationHelper.rotateX(rotateX)
self.rotateY(rotateY)
var cameraRot = self.rotationHelper.rotationDegrees()
cameraRot.x = clamp(cameraRot.x, -70, 70)
self.rotationHelper.rotationDegrees = cameraRot
proc inputWASD(): Vector2 =
result = vec2()
if Input.isActionPressed("movement_forward"):
result.y += 1
if Input.isActionPressed("movement_backward"):
result.y -= 1
if Input.isActionPressed("movement_left"):
result.x -= 1
if Input.isActionPressed("movement_right"):
result.x += 1
proc flash() =
let light = self.flashlight
if light.isVisibleInTree():
light.hide()
else:
light.show()
proc uiCancel() =
if Input.getMouseMode() == Input.MOUSE_MODE_VISIBLE:
Input.setMouseMode(Input.MOUSE_MODE_CAPTURED)
else:
Input.setMouseMode(Input.MOUSE_MODE_VISIBLE)
proc getAccel(hvel: Vector3): float =
result = DEACCEL
if self.dir.dot(hvel) > 0:
if self.isSprinting:
result = SPRINT_ACCEL
else:
result = ACCEL
おわりに
Godot + Nimの解説は探してもあんまりないんですが、Godot自体は結構みつかりました(youtubeとか)
なので勉強を続けて、1本ゲームを完成させたいです
あと、今回はNim使いましたが、
Godot公式がオススメしているGDScriptも書きやすそうなので(pythonっぽい&静的型付け)使ってみたいです