CameraCon

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

12 Likes

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

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

Should be local?

1 Like

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.

4 Likes