mieki256's diary



2020/04/24(金) [n年前の日記]

#1 [godot] カメラ情報から自機の移動範囲を求めるソレがたぶんできた気がする

Godoto Engine 3.2.1 上で、カメラ情報から自機の移動範囲を求めることができないかと考えていたけれど。たぶんなんとかなったような気がする。

図を再掲。要は、カメラから見える範囲 ―― 以下の赤い範囲を求めたい。

3dareacheck_ss03.png

以下のような手順で考えれば、範囲を求められるんじゃないかなと…。

fov_calc_02_1.png

  1. カメラの位置が原点にあると仮定して、カメラの四隅、左上と右下の線分を作る。
  2. その線分を、x軸に沿って回転させる。
  3. 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で計算してしまった…。

何にせよ、やはり中学レベルの数学で全然解ける問題だったなと…。

しかしコレ、もっと簡単に求める方法は無いのだろうか。なんとなくだけど、ありそうな気がする。

以上です。

過去ログ表示

Prev - 2020/04 - Next
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project