2. Movement tutorial

2020. 6. 18. 12:47DEFOLD/튜토리얼

키보드 입력을 할 때 자연스럽게 움직이는 플레이어 제어 우주선을 만드는 단계를 안내합니다. 이 학습서를 완료하면 다음 질문에 대한 답을 알게됩니다.

 

  • 벡터 란 무엇입니까?
  • 벡터를 사용하여 위치, 속도 및 가속도를 나타내는 방법은 무엇입니까?
  • 이것을 사용하여 실험하고 더 발전시킬 수있는 게임 프로토타입을 만드는 방법은 무엇입니까?

속도 및 가속도와 같은 물리학 개념에 대한 기본적인 이해가 있다고 가정합니다. Lua 프로그래밍에 대한 기본적인 이해가 필요합니다.

 

이 프로젝트는 미리 준비되어 있으므로 귀찮게 할 설정이 없습니다. 게임을 실행하여 작업해야 할 사항에 대한 개요를 얻으십시오.

 

  • 여기에는 애니메이션 우주선과 배경 그래픽이 포함됩니다.
  • 화살표 키와 마우스 클릭에 대한 입력이 설정됩니다.
  • 스크립트가 첨부 된 "우주선"게임 개체가 있습니다.
  • 이 스크립트에는 플레이어 입력에 반응하는 코드가 있습니다. 처음에는 입력에 대한 반응으로 메시지를 콘솔에 인쇄합니다.

게임을 실행 한 상태에서 화살표 버튼을 누르거나 게임 창에서을 클릭 한 다음 편집기 콘솔에서 입력 결과를 확인하십시오. 누른 버튼에 따라 다른 텍스트가 콘솔에 인쇄됩니다.

Move SpaceShip

세부 사항을 파고 들기 전에 먼저 간단한 실험을 하고 우주선 이동을 하도록 합시다

"spaceship.script"를 열고 on_input()함수에 아래와 같이 코드 작성을 진행합니다.

function on_input(self, action_id, action)
    if action_id == hash("up") then
        local p = go.get_position()
        p.y = p.y + 1
        go.set_position(p)
    elseif...

게임을 다시 시작하고 up 화살표 버튼을 누르면 어떻게 우주선이 움직이는지 확인합니다.

 

사용자의 입력이 발생시 on_input()은 "/input/game.input_binding"파일에 정의된 값을 action_id 파라메터에 바인딩되어서 들어옵니다. 그리고 이것은 기본적으로 초당 60프레임의 1프레임당 실행이 됩니다.

 

go.get_position은 게임 오브젝트 위치를 가져오며 별도 인수가 없을 경우 현재 게임 오브젝트의 위치를 반환합니다. 그리고 반환값은 vector3를 반환합니다.  vector3는 x, y, z좌표의 값을 가지고 있으며 up키를 누를 경우 위로 이동해야 하기 때문에 y 좌표 값이 1 증가하도록 작성 했습니다.

 

그리고 해당 게임오브젝트의 위치가 변경이 되어야 하니 수정된 vector값을 set_position()함수에 전달합니다.

 

p.y + 1을 +5로 수정하면 더 빠르게 이동됨을 확인 할 수 있습니다.

Vector

벡터는 방향과 크기(길이)를 갖는 수학적 엔티티입니다 . 벡터는 벡터 공간의 특정지점을 나타냅니다. 실제로 벡터는 점에 좌표를 제공하는 일련의 숫자로 구성됩니다. 2차원 공간 (평면)에서 벡터를 나타내는 데 두개의 숫자가 필요합니다. 하나는 X 축 값이고 하나는 Y 축 값입니다.

 

3차원 공간에서는 3개의 숫자가 필요합니다. X, Y, Z

 

 

백터의 v의 크기 또는 길이는 피타고라스 정리를 사용하여 계산이 됩니다.

 

크기가 1인 벡터를 정규화된(Nomalize) 벡터라고 합니다.

 

Defold에는 2D 게임에 적합한 툴셋이 있지만 엔진은 실제로 3D 엔진입니다. 모든 게임 개체 및 구성 요소는 vector3개체로 표현 된 위치와 함께 3D 공간에 배치 됩니다. 게임을 2D로 볼 때 X 및 Y 값은 "너비"및 "높이"축을 따라 개체의 위치를 ​​결정하고 Z 위치는 "깊이"축을 따라 위치를 결정합니다. Z 위치를 사용하면 겹치는 객체의 가시성을 제어 할 수 있습니다. Z 값이 1 인 스프라이트는 Z 위치 0에서 스프라이트 앞에 나타납니다. 기본적으로 Defold는 -1과 1 사이의 Z 값을 허용하는 좌표계를 사용합니다.

 

 

Defold Lua 라이브러리 vmath에는 vector3객체 를 생성하고 조작하는 기능이 있습니다 .

-- create a new vector3 at X position 100 and Y position 350.
local position = vmath.vector3(100, 350, 0)

-- set the position of game object "player" to the new vector.
go.set_position(position, "player")

보다 큰 차원의 벡터도 가능합니다. Defold는 vector4는 4가지 구성 요소가있는 객체를 사용 하여 색상을 인코딩합니다. 

처음 세 구성 요소는 빨강, 녹색, 파랑의 값을 제공하고 마지막 구성 요소는 "알파"라고도하는 반투명 정도를 제공합니다.

 

일상 생활에서는 스칼라 값, 숫자 라인의 점을 나타내는 실수를 사용하여 산술을 수행하는 데 사용됩니다. 우리는 많은 다른 것을 의미하기 위해 스칼라를 사용합니다. 숫자 12는 미터, 킬로그램, 파운드, 초, 초당 미터, 볼트 또는 달러를 의미 할 수 있습니다. 벡터에 대해서도 마찬가지입니다. 이미 벡터를 사용하여 객체의 위치를 ​​설명하는 방법을 살펴 보았습니다. 또한 공간을 통한 객체의 움직임을 설명하는 데 매우 좋습니다.

 

컴퓨터 화면(2D 평면)에서 모션을 설명하려면 다음 두 가지 값이 필요하다. X축을 따라가는 속도와 Y축을 따라가는 속도. 두 개의 개별 스칼라 값을 매우 잘 사용하고 X와 Y 위치에 별도로 속도 값을 추가할 수 있다.

position_x = position_x + speed_x * elapsed_seconds
position_y = position_y + speed_y * elapsed_seconds

이것은 이전에 우주선을 위로 움직이게했을 때와 거의 같은 일이며, 이와 같은 계산 동작에는 아무런 문제가 없습니다. 

그러나 벡터를 사용하면 모션을보다 선명하고 간결하게 표현할 수 있습니다. 벡터는 방향  크기를 나타내므로 움직임에 직관적으로 맞습니다. 벡터의 방향은 움직임의 방향과 같고 크기는 움직임의 양을 나타냅니다.

position = position + speed * elapsed_seconds

위치 값과 속도 값이 같은 공간에서 벡터로 표현되는 한 더하기 빼기, 스칼라 값을 곱하여 스케일링할 수 있다. 이 연산들은 벡터 대수학의 중심 부분이다.

Vector algebra

벡터 대수는 벡터에 대한 수학적 연산을 정의합니다. 가장 간단한 부정, 덧셈 및 뺄셈으로 시작합니다.

Negation(부정) : -v로 표시된 벡터 v를 부정하면 벡터의 각 성분이 부정된다. 이렇게 하면 동일한 크기로 원래 벡터의 반대 방향을 가리키는 벡터가 된다.

 

Addition(추가) : u + v로 표시된 벡터 v에 벡터 u를 추가하면 u의 각 구성요소가 v에 추가된다. 그 결과는 다음과 같은 새로운 벡터:

 

벡터는 종종 좌표계로부터 대체되어 다음과 같은 작업을 명확하게 한다.

 

 

Subtraction(뺄셈) : u - v로 표시된 벡터 u에서 벡터 v를 빼는 것은 v의 부정을 u에 추가하는 것과 같다. so u - v = u + (-v):

Multiplication with scalar(스칼라를 이용한 곱하기) : 벡터 v에 실제 숫자 r을 곱하면 규모 r과 함께 새로운 벡터가 생성된다. 벡터는 요인 r에 의해 줄무늬가 된다. -r로 곱하면 방향이 180도 뒤집힌다.

이것은 벡터에 대한 기본적인 작업이며, 항상 사용하게 될 것이다. 또한, 예를 들어 두 벡터가 서로 평행인지 직각인지 확인하려면 다음과 같은 두 가지 특수 작업이 유용하다.

Dot product : u ∙ v로 표시된 벡터 u와 v 두 개의 도트 제품은 스칼라 값이다. 이는 다음과 같이 정의된다.

 

‖u‖은 벡터 u의 크기다.
‖v‖는 벡터 v의 크기다.
θ은 벡터 사이의 각도다.

 

벡터가 직교 인 경우 (그 사이의 각도는 90도) 내적은 0입니다.

외적 두 벡터의 외적 U를  V 에 의해 표시 U × V는 모두에 수직 인 벡터이고, U  V :

 

다음과 같은 경우 결과 벡터는 0입니다.

  • 입력 벡터 중 하나 또는 둘 모두가 0 벡터입니다 ( u = 0 또는 v = 0).
  • 두 입력 벡터는 평행합니다 (θ = 0 °)
  • 두 개의 입력 벡터는 반 평행 (θ = 180 °)입니다.

백터로 움직임 만들기

벡터 대수를 사용하여 이제 우주선의 움직임을 간단하게 다시 작성할 수 있습니다.

"spaceship.script"를 열고 init(), update()및 on_input()함수를 수정하십시오 .

function init(self)
	msg.post(".", "acquire_input_focus")
	self.input = vmath.vector3() -- (1)
end

(1) vector3입력 방향을 저장할 0값을 가진 백터를 만듭니다 . 현재 스크립트 인스턴스(self)에 배치되어 우주선 게임 오브젝트의 수명 동안 사용할 수 있습니다.

 

function update(self, dt)
	local movement = self.input * 3 -- (1)
	local p = go.get_position() -- (2)
	go.set_position(p + movement) -- (3)
	self.input = vmath.vector3() -- (4)
end

(1) 플레이어의 입력 벡터를 기반으로 움직임 벡터를 계산합니다.

(2) 현재 게임 오브젝트의 위치 (우주선)를 검색합니다. 위치 값은 vector3입니다.

(3) 현재 게임 오브젝트의 위치에 p이동 벡터  더한 값으로 설정하십시오.

(4) 입력 벡터를 0으로 만듭니다. on_input() 함수는 업데이트() 전 각 프레임이라고 하며 입력 벡터를 설정할 책임이 있다.

function on_input(self, action_id, action)
	if action_id == hash("up") then
		self.input.y = 1 -- (1)
	elseif action_id == hash("down") then
		self.input.y = -1 -- (1)
	elseif action_id == hash("left") then
		self.input.x = -1 -- (1)
	elseif action_id == hash("right") then
		self.input.x = 1 -- (1)
	elseif action_id == hash("click") and action.pressed then
		print("CLICK!")
	end
end

(1) 플레이어 입력에 따라 입력 벡터의 x 또는 y 구성 요소를 설정하십시오. 플레이어 누르면 up과 left동시에, 상기 함수를 두 번 호출 될 두 부품은 대각선 방향 입력 결과 설정된다.

 

이 코드에는 두 가지 문제가 있습니다.

 

먼저, 수직 또는 수평으로 이동하는 경우 입력 벡터의 길이는 1이지만 대각선 길이는 1.4142 (제곱근 2)이므로 대각선 이동이 더 빠릅니다. 당신은 아마 그것을 원하지 않을 것입니다.

 

둘째, 속도의 단위는 프레임 길이에 관계없이 픽셀/프레임으로 표현됩니다. 각 프레임마다 3픽셀씩 움직입니다 (또는 대각선으로 약 4.2). 배를 더 빨리 움직이려면 3을 더 높은 값으로 변경하십시오. 느리게하려면 값을 줄이십시오. 속도를 픽셀/초로 표현할 수 있다면 더 좋습니다.

 

첫 번째 문제는 해결하기 쉽고 입력 벡터를 정규화하여 입력 길이가 항상 1입니다.

function update(self, dt)
	if vmath.length_sqr(self.input) > 1 then -- 대각선 이동이면
		self.input = vmath.normalize(self.input) -- 정규화한다.
	end
	local movement = self.input * 3
	local p = go.get_position()
	go.set_position(p + movement)
	self.input = vmath.vector3()
end

입력 벡터의 제곱 길이가 1보다 크면 벡터를 정규화하여 크기가 1이 되도록 합니다. 길이와 비교하는 것보다 빠르기 때문에 제곱 길이와 비교하십시오. 두 번째 문제는 Time step값을 사용해야합니다.

Time step

Defold 엔진은 각 프레임마다 스크립트의 update() 함수를 호출합니다. Defold 게임은 보통 초당 60 프레임으로 실행되므로 각 프레임의 길이는 0.016666초입니다. 그것은 프레임마다 update()를 호출한 경과한 시간이다. 크기 3의 속도 벡터는 초당 60개의 프레임이 실제로 존재하는 한 1초당 3 * 60 = 180 픽셀의 속도를 나타낸다(일반 렌더 스크립트 포함). 어떤 이유로든,  framerate에 차질이 생긴다면 어떻게 될까? 현재의 코드 이동은 고르지 못하고 예측할 수 없을 것이다.

 

초당 픽셀로 작업하면 가변 프레임(framrate)을 적절하게 사용할 수 있으며, 스톱워치로 게임을 측정하고 거리와 타이밍에 대한 이유를 더 나은 방법으로 파악할 수 있다.

Defold는 업데이트() 함수에 Time step 파라메터를 제공한다. 이 파라메터는는 보통 dt("delta time"의 경우)라고 하며, 그 값은 마지막 프레임 이후 경과한 초 수입니다. dt에 대해 속도를 조절하면 적절한 단위를 얻을 수 있다.

function update(self, dt)
	if vmath.length_sqr(self.input) > 1 then
		self.input = vmath.normalize(self.input)
	end
	local movement = self.input * 150 * dt  -- (1)
	local p = go.get_position()
	go.set_position(p + movement)
	self.input = vmath.vector3()
end

(1) 현재 속도는 초당 150픽셀이다. 화면 폭은 1280픽셀이므로 배를 가로질러 비행하는데 8.53초가 걸릴 것이다. 그것을 스톱워치로 확인할 수 있다.

 

게임을 다시 실행하고 이동 코드를 시도하십시오. 이 단계에서는 작동하지만 뻣뻣하고 그다지 역동적이지 않다. 우주선에 무게감을 주는 것이 좋은 방법은 속도 대신 가속도를 바꿔 플레이어의 입력 제어 운동을 하는 것이다.

Acceleration

위의 코드에서 속도는 일정한 값으로 설정되었는데, 이는 시간 단계(dt)에 걸쳐 작용하는 속도의 결과 이동 또는 변환을 시간 단계와 곱하여 계산할 수 있다는 것을 의미한다: 이동거리 = 속도 * dt 또는 다음 도표의 주황색 영역:

 

Acceleration(가속)은 어떤 것이 속도와 방향을 얼마나 빨리 바꾸는지 정의한다. 가속은 프레임 시간 단계(dt)에 걸쳐 작용한 다음 속도에 추가된다. 속도는 프레임 위에 작용하고 그에 따른 움직임이 그 위치에 추가된다. 시간이 지남에 따라 속도가 변화하기 때문에 움직임은 곡선 아래의 영역으로 계산되어야 한다. 수학에서는 이것을 시간의 경과에 따른 통합이라고 한다.

시간 스텝이 작을 경우 v0과 v1 사이에 작용하는 가속도가 일정하다고 가정하여 면적의 좋은 기하학적 근사치를 계산할 수 있으며, 이는 속도가 두 점 사이에서 선형적으로 변화한다는 것을 의미한다. 이러한 가정에 의해 v1은 v0 + 가속 * dt로 계산될 수 있으며, 그에 따른 이동은 다음과 같이 된다.

이제 init() 및 update()에 대한 최종 코드를 작성할 수 있다(on_properties에 대한 코드는 그대로 유지됨).

 

function init(self)
	msg.post(".", "acquire_input_focus")
	self.velocity = vmath.vector3() -- (1)
	self.input = vmath.vector3()
end

function update(self, dt)
	if vmath.length_sqr(self.input) > 1 then
		self.input = vmath.normalize(self.input)
	end

	local acceleration = self.input * 200  -- (2)

	local dv = acceleration * dt  -- (3)
	local v0 = self.velocity  -- (4)
	local v1 = self.velocity + dv -- (5)

	print((v0 + v1))
	
	local movement = (v0 + v1) * dt * 0.5 -- (6)
	local p = go.get_position()
	go.set_position(p + movement)

	self.velocity = v1
	self.input = vmath.vector3()
end

function on_input(self, action_id, action)
	if action_id == hash("up") then
		self.input.y = 1 -- (1)
	elseif action_id == hash("down") then
		self.input.y = -1 -- (1)
	elseif action_id == hash("left") then
		self.input.x = -1 -- (1)
	elseif action_id == hash("right") then
		self.input.x = 1 -- (1)
	elseif action_id == hash("click") and action.pressed then
		print("CLICK!")
	end
end

실행해보고 이상없이 동작하는지 확인해 봐라

 

그리고 아래의 예제를 구현해봐라

 

1. 속도를 제한 하십시오

2. 우주선이 화면 가장 자리에서 튕겨 나가게 해라

3. 마우스 클릭을 허용하여 마우스 클릭 방향으로 가도록 하게 한다.

 

 

'DEFOLD > 튜토리얼' 카테고리의 다른 글

4. War battles tutorial  (0) 2020.06.22
3. Colorslide tutorial  (0) 2020.06.18
1. Walking astronaut tutorial  (0) 2020.06.18