2020/04/24(金) [n年前の日記]
#1 [godot] カメラ情報から自機の移動範囲を求めるソレがたぶんできた気がする
Godoto Engine 3.2.1 上で、カメラ情報から自機の移動範囲を求めることができないかと考えていたけれど。たぶんなんとかなったような気がする。
図を再掲。要は、カメラから見える範囲 ―― 以下の赤い範囲を求めたい。
以下のような手順で考えれば、範囲を求められるんじゃないかなと…。
試しにPythonを使って仮で計算してみた。
_calc_fov_area.py
求めた値を使って、Godot Engine で自機の位置を変えてみたけど、カメラの隅に配置できてる感じに見えたので、たぶん計算は合ってるんじゃないかな…。
Godot Engine の GDScript で書くと、以下のような感じ。
btopleft と bbottomright に、自機の移動範囲の左上座標、右下座標が入る。みたいな。
x軸に沿って回転させるあたり、おそらく Godot Engine はソレ用のメソッドを持ってると思うのだけど、どれがソレなのか分からなかったので、sinとcosで計算してしまった…。
何にせよ、やはり中学レベルの数学で全然解ける問題だったなと…。
しかしコレ、もっと簡単に求める方法は無いのだろうか。なんとなくだけど、ありそうな気がする。
図を再掲。要は、カメラから見える範囲 ―― 以下の赤い範囲を求めたい。
以下のような手順で考えれば、範囲を求められるんじゃないかなと…。
- カメラの位置が原点にあると仮定して、カメラの四隅、左上と右下の線分を作る。
- その線分を、x軸に沿って回転させる。
- zx面、xy面で考えて、自機が移動する平面(y座標が一定)との交点を求める。z値とx値が得られる。
試しにPythonを使って仮で計算してみた。
_calc_fov_area.py
import math
def get_rot_pos(x, y, ang):
a = math.radians(ang)
xr = x * math.cos(a) - y * math.sin(a)
yr = x * math.sin(a) + y * math.cos(a)
return xr, yr
def dump_pos(p, s):
print("%s = (%f, %f, %f)" % (s, p[0], p[1], p[2]))
def get_fov_area(cx, cy, cz, cxrot, fov, w, h):
print("Camera pos = (%f, %f, %f)" % (cx, cy, cz))
print("Camera x rot = %f" % cxrot)
print("Camera fov = %f" % fov)
print("Window size = %d x %d" % (w, h))
d = (h/2) / math.tan(math.radians(fov/2))
p1 = (-w/2, h/2, -d)
p2 = (w/2, -h/2, -d)
# Rotate by Camera angle
z, y = get_rot_pos(p1[2], p1[1], -cxrot)
p1r = (p1[0], y, z)
z, y = get_rot_pos(p2[2], p2[1], -cxrot)
p2r = (p2[0], y, z)
y = -cy
xr, yr, zr = p1r
z = y * zr / yr + cz
x = y * xr / yr
topleft = (x, 0, z)
xr, yr, zr = p2r
z = y * zr / yr + cz
x = y * xr / yr
bottomright = (x, 0, z)
dump_pos(p1, "p1")
dump_pos(p2, "p2")
dump_pos(p1r, "p1r")
dump_pos(p2r, "p2r")
dump_pos(topleft, "topleft")
dump_pos(bottomright, "bottomright")
get_fov_area(0, 40, 12, -65, 60, 1280, 720)
> python calc_fov_area.py Camera pos = (0.000000, 40.000000, 12.000000) Camera x rot = -65.000000 Camera fov = 60.000000 Window size = 1280 x 720 p1 = (-640.000000, 360.000000, -623.538291) p2 = (640.000000, -360.000000, -623.538291) p1r = (-640.000000, -412.975034, -589.789472) p2r = (640.000000, -717.260183, 62.752135) topleft = (-61.989219, 0.000000, -45.125920) bottomright = (35.691372, 0.000000, 15.499547)
求めた値を使って、Godot Engine で自機の位置を変えてみたけど、カメラの隅に配置できてる感じに見えたので、たぶん計算は合ってるんじゃないかな…。
Godot Engine の GDScript で書くと、以下のような感じ。
var camera:Camera
var btopleft = Vector3()
var bbottomright = Vector3()
func _ready():
camera = get_tree().root.get_node("Level-Spatial/Camera")
# get project display width and height
disparea = Vector2(
ProjectSettings.get_setting("display/window/size/width"),
ProjectSettings.get_setting("display/window/size/height")
)
_calc_move_area(camera, disparea.x, disparea.y)
func _get_rot_pos(x, y, ang):
var a = deg2rad(ang)
var xr = x * cos(a) - y * sin(a)
var yr = x * sin(a) + y * cos(a)
return Vector2(xr, yr)
func _get_border(p:Vector3, rotx, y, cz):
var v2:Vector2 = _get_rot_pos(p.z, p.y, rotx)
var pr = Vector3(p.x, v2.y, v2.x)
return Vector3(
y * pr.x / pr.y,
0,
y * pr.z / pr.y + cz
)
func _calc_move_area(camera:Camera, w, h):
var cpos:Vector3 = camera.translation
var crot:Vector3 = camera.rotation_degrees
var fov = camera.fov
var d = (h/2) / tan(deg2rad(fov/2))
var p1 = Vector3(-w/2, h/2, -d)
var p2 = Vector3(w/2, -h/2, -d)
var q1:Vector3 = _get_border(p1, -crot.x, -cpos.y, cpos.z)
var q2:Vector3 = _get_border(p2, -crot.x, -cpos.y, cpos.z)
btopleft = q1
bbottomright = q2
btopleft と bbottomright に、自機の移動範囲の左上座標、右下座標が入る。みたいな。
x軸に沿って回転させるあたり、おそらく Godot Engine はソレ用のメソッドを持ってると思うのだけど、どれがソレなのか分からなかったので、sinとcosで計算してしまった…。
何にせよ、やはり中学レベルの数学で全然解ける問題だったなと…。
しかしコレ、もっと簡単に求める方法は無いのだろうか。なんとなくだけど、ありそうな気がする。
[ ツッコむ ]
以上、1 日分です。

