CameraCon

#1

Thanks to the new mesh features I’m inspired to work with 3D again and relearning / making useful things for me. Here is a very basic camera view movement and rotation script. If you setup the proper keys in input and attach this script to a GO which has our camera on you can use it to move around a 3D scene in a way useful for general inspection.

V1 because it’s a first fresh attempt and I intend to make it fancier in future versions. :sunglasses: If you have a similar kind of script please contribute! I would like to make versions of every popular camera style. I’ll turn this into a proper project later.

V1

local turn_speed = 0.01
local move_speed = 0.25

local forward = vmath.vector3(0,0,-1)
local backward = vmath.vector3(0,0,1)
local left = vmath.vector3(-1,0,0)
local right = vmath.vector3(1,0,0)


local function move(v)
	local position = go.get_position()
	local rotation = go.get_rotation()
	local direction =  vmath.rotate(rotation, v)
	position = position + direction * move_speed
	go.set_position(position)
end

function init(self)
	msg.post(".", "acquire_input_focus")
end

function final(self)
	-- Add finalization code here
	-- Remove this function if not needed
end

function update(self, dt)
	-- Add update code here
	-- Remove this function if not needed
end

function on_message(self, message_id, message, sender)
	-- Add message-handling code here
	-- Remove this function if not needed
end

function on_input(self, action_id, action)
	if action_id == hash("touch") and action.released == false then
		local rotation = go.get_rotation()
		local direction_x
		local direction_y
		if action.dx then
			if action.dx > 0 then
				rotation = rotation * vmath.quat_axis_angle(vmath.vector3(0,-1,0), turn_speed * math.abs(action.dx))
			else
				rotation = rotation * vmath.quat_axis_angle(vmath.vector3(0,1,0), turn_speed * math.abs(action.dx))
			end
		end

		if action.dy then
			if action.dy > 0 then
				rotation = rotation * vmath.quat_axis_angle(vmath.vector3(1,0,0), turn_speed * math.abs(action.dy))
			else
				rotation = rotation * vmath.quat_axis_angle(vmath.vector3(-1,0,0), turn_speed * math.abs(action.dy))
			end
		end
		go.set_rotation(rotation)
	end

	if action_id == hash("key_w") and action.released == false then
		move(forward)
	end
	if action_id == hash("key_s") and action.released == false then
		move(backward)
	end
	if action_id == hash("key_a") and action.released == false then
		move(left)
	end
	if action_id == hash("key_d") and action.released == false then
		move(right)
	end	
end

function on_reload(self)
	-- Add reload-handling code here
	-- Remove this function if not needed
end

11 Likes

#2

V2 adds camera smoothing and removes roll from rotation. I’m not yet sure how to remote the general unwanted (in this case) roll introduced from rotating while in rotations outside of a normal FPS camera view?

Next version will be a rewrite using euler angles for setting roll / pitch / yaw so it’s easier to control.

local turn_speed = 0.6
local move_speed = 0.25

local FORWARD = vmath.vector3(0,0,-1)
local BACKWARD = vmath.vector3(0,0,1)
local LEFT = vmath.vector3(-1,0,0)
local RIGHT = vmath.vector3(1,0,0)
local UP = vmath.vector3(0,1,0)
local DOWN = vmath.vector3(0,-1,0)


local function move(v)
	local position = go.get_position()
	local rotation = go.get_rotation()
	local direction =  vmath.rotate(rotation, v)
	position = position + direction * move_speed
	go.set_position(position)
end

function init(self)
	msg.post(".", "acquire_input_focus")
	self.rotation = go.get_rotation()

end

function final(self)
	-- Add finalization code here
	-- Remove this function if not needed
end

function update(self, dt)
	-- print(self.touch_dx, self.touch_dy)
	if self.touch_released == false then
		local rotation = go.get_rotation()
		local direction_x
		local direction_y
		
		if self.touch_dx then
			if self.touch_dx > 0 then
				rotation = vmath.quat_axis_angle(DOWN, dt * turn_speed * math.abs(self.touch_dx)) * rotation
			else
				rotation = vmath.quat_axis_angle(UP, dt * turn_speed * math.abs(self.touch_dx)) * rotation
			end
		end

		if self.touch_dy then
			if self.touch_dy > 0 then
				rotation = rotation * vmath.quat_axis_angle(RIGHT, dt * turn_speed * math.abs(self.touch_dy))
			else
				rotation = rotation * vmath.quat_axis_angle(LEFT, dt * turn_speed * math.abs(self.touch_dy))
			end
		end
		self.rotation = rotation
		go.set_rotation(vmath.slerp(0.30, go.get_rotation(), self.rotation))
	else
		go.set_rotation(vmath.slerp(0.90, go.get_rotation(), self.rotation))
	end

	self.touch_released = false
	self.touch_dx = nil
	self.touch_dy = nil
end

function on_message(self, message_id, message, sender)
	-- Add message-handling code here
	-- Remove this function if not needed
end

function on_input(self, action_id, action)

	if action_id == hash("touch") then
		self.touch = true
		-- pprint(action)
		self.touch_dx = action.dx
		self.touch_dy = action.dy
		-- print(self.touch_dx, self.touch_dy)
		if action.released == false then
			self.touch_released = false
		else
			self.touch_released = true
		end
	else
		self.touch = false
	end

	if action_id == hash("key_w") and action.released == false then
		move(FORWARD)
	end
	if action_id == hash("key_s") and action.released == false then
		move(BACKWARD)
	end
	if action_id == hash("key_a") and action.released == false then
		move(LEFT)
	end
	if action_id == hash("key_d") and action.released == false then
		move(RIGHT)
	end	
end

function on_reload(self)
	-- Add reload-handling code here
	-- Remove this function if not needed
end

7 Likes

#3

V3 changes rotation method to euler angles, and moves movement into update instead of input. It also now constrains up/down rotation view. It’s a bit hacky at the moment.

Next version will add pseudo physics to the movement?

-- 90
-- 75
-- 54-68 viewmodel

local turn_speed = 0.6
local move_speed = 0.25
local run_mod = 2.5

local FORWARD = vmath.vector3(0,0,-1)
local BACKWARD = vmath.vector3(0,0,1)
local LEFT = vmath.vector3(-1,0,0)
local RIGHT = vmath.vector3(1,0,0)
local UP = vmath.vector3(0,1,0)
local DOWN = vmath.vector3(0,-1,0)


local function wrap(number, bound)
	if (number < 0) then
		return bound - (math.fmod(-number, bound))
	else
		return math.fmod(number, bound)
	end
end

local function bound(number, min, max)
	if number < min then 
		number = min
	elseif number > max then
		number = max
	end
	return number
end

local function quat_from_euler_vector(vect)
	local zq = vmath.quat_rotation_z(math.rad(vect.z))
	local zx = vmath.quat_rotation_x(math.rad(vect.x))
	local zy = vmath.quat_rotation_y(math.rad(vect.y))
	return zy * zx * zq
end


local function move(v, if_run)
	local position = go.get_position()
	local rotation = go.get_rotation()
	local direction =  vmath.rotate(rotation, v)
	if not if_run then
		position = position + direction * move_speed
	else
		position = position + direction * move_speed * run_mod
	end
	go.set_position(position)	
end

function init(self)
	msg.post(".", "acquire_input_focus")
	self.rotation = go.get_rotation()
	self.euler_rotation = vmath.vector3(0,0,0)
	self.rotation = quat_from_euler_vector(self.euler_rotation)
	self.move_vector = vmath.vector3(0,0,0)
	go.set_rotation(vmath.slerp(0.90, go.get_rotation(), self.rotation))
end

function final(self)
	-- Add finalization code here
	-- Remove this function if not needed
end

function update(self, dt)
	if self.touch_released == false then
		if self.touch_dx then
			self.euler_rotation.y = self.euler_rotation.y + self.touch_dx * -turn_speed
			if self.euler_rotation.y > 360 then
				self.euler_rotation.y = self.euler_rotation.y - 360
			end
			if self.euler_rotation.y < -360 then
				self.euler_rotation.y = self.euler_rotation.y + 360
			end
		end

		if self.touch_dy then
			self.euler_rotation.x = self.euler_rotation.x + self.touch_dy * turn_speed
			self.euler_rotation.x = bound(self.euler_rotation.x, -90, 90)
		end
		self.rotation = quat_from_euler_vector(self.euler_rotation)
		go.set_rotation(vmath.slerp(0.30, go.get_rotation(), self.rotation))
	else
		go.set_rotation(vmath.slerp(0.90, go.get_rotation(), self.rotation))
	end

	if vmath.length(self.move_vector) > 0 then
		move(vmath.normalize(self.move_vector), self.key_shift)
	end

	self.touch_released = false
	self.touch_dx = nil
	self.touch_dy = nil
	self.key_shift = false
	self.move_vector.x = 0
	self.move_vector.y = 0
	self.move_vector.z = 0
end

function on_message(self, message_id, message, sender)
	-- Add message-handling code here
	-- Remove this function if not needed
end

function on_input(self, action_id, action)

	if action_id == hash("touch") then
		self.touch = true
		-- pprint(action)
		self.touch_dx = action.dx
		self.touch_dy = action.dy
		-- print(self.touch_dx, self.touch_dy)
		if action.released == false then
			self.touch_released = false
		else
			self.touch_released = true
		end
	else
		self.touch = false
	end

	if action_id == hash("key_shift") and action.released == false then
		self.key_shift = true
	end	

	if action_id == hash("key_w") and action.released == false then
		self.move_vector.z = self.move_vector.z - 1
	end
	if action_id == hash("key_s") and action.released == false then
		self.move_vector.z = self.move_vector.z + 1
	end
	if action_id == hash("key_a") and action.released == false then
		self.move_vector.x = self.move_vector.x - 1
	end
	if action_id == hash("key_d") and action.released == false then
		self.move_vector.x = self.move_vector.x + 1
	end	


end

function on_reload(self)
	-- Add reload-handling code here
	-- Remove this function if not needed
end

5 Likes

#4

Should be local?

1 Like

#5

Here is a sample of this script in action.

I will be working on making CameraCon into an accessible independent module with examples along with a new first person and third person example projects in the future with samples of various types of interaction. One issue that I will be waiting on is https://github.com/defold/defold/issues/4819 but when that happens I will go full steam ahead on many 3D resources for the community.

3 Likes