The camera system in ITSP was made with Lua scripting.
-- turn this on to view the in game debug drawing
local cameraDebugDraw = true
-- when the camera target enters a point of interest, a certain amount of influence will be put on the
-- camera to go towards the point of interest instead of the camera target.
local currPointOfInterestPercentage = 1
local toNewPosX = 0 -- we will be moving the camera to a desired location, this is the movement on the x axis
local toNewPosY = 0 -- camera movement on the y axis
local currRCamOffset = {x = 0, y = 0, z = 0} --[ryan] this is the amount of offset we will put on the camera target due to right stick input.
-- we want to move the right stick influence smoothly and accurately, so we are keeping track of the last 100 frames of right stick input.
-- this way if the player quickly fires a shot in a different direction, we don't swing the camera around wildly.
local averageSticks = {}
local numSticks = 100 -- the amount of frames we are going to keep track of
local totalSticks = {x = 0,y = 0} -- the total amount of stick input used for making the average
local lastStickHead = {x = 0,y = 0} -- we can just remove one stick, and add a new stick value
local averageStick = {x = 0,y = 0} -- the average stick value used for calculating right stick camera offset
local stickHead = 1 -- instead of reordering an array, we will just overwrite what is currently the last indexed value
-- initialize all the stick inputs to 0
for i = 1, numSticks do
averageSticks[i] = {x = 0,y = 0}
end
local lStickCameraInfluence = 1 -- we are going to move the camera based of the velocity of the camera target
local rStickCameraInfluence = 6 -- we need to weight the amount we use for right stick influence vs velocity influence
local toSmartCamZoomSpeed = 0.35 -- this is how fast the camera will change it's zoom
local cameraMoveSpeed = 0.05 -- this is the base speed that the camera will use to move to it's desired position
local toPlayerInputSpeed = 0.075 -- we want to move the camera a little faster to the player input position
-- we can't let the camera target get off screen, this is the safe zone border we will be using in screen space
local safeZoneX = 513
local safeZoneY = 288
function updateSmartCamera(ms,currCameraTarget)
local targetVel = currCameraTarget:getVelocity()
local targetPos = currCameraTarget:getPosition()
local newCamPos = currCameraTarget:getPosition() -- this will end up being the position of the camera
-- retrieve the inputs from the camera target
local lStickX = currCameraTarget.lStickX
local lStickY = currCameraTarget.lStickY
local rStickX = currCameraTarget.rStickX
local rStickY = currCameraTarget.rStickY
-- if the camera target has a shooter, then we are going to use the shooters input values, this is used for the missile gates.
if(currCameraTarget.shooter ~= nil) then
lStickX = currCameraTarget.shooter.lStickX
lStickY = currCameraTarget.shooter.lStickY
rStickX = currCameraTarget.shooter.rStickX
rStickY = currCameraTarget.shooter.rStickY
end
-- initialize the destination variables
local desiredStickPosition = currCameraTarget:getPosition()
local dummyCamPos = dummyCameraTarget:getPosition()
local smartCamPos = smartCameraTarget:getPosition()
-- we need to find out where the points of interest are, because the smart camera target will move
-- based on how close to a POI you are. The point of interest will tell the camera target how much camera influence it desires.
-- If the camera is in more than one POI, we need to average those amounts to put the camera in the correct position.
local pointOfInterestPos = nil
local pointOfInterestPercentage = 1
local avgCamPos = { x = 0, y = 0, z = 0}
local poiCount = 0
local totalPointOfInterestInfluence = 0
local currentPOIZoom = 0
-- the idea here is that we can't just take the average between all the points of interest position alone.
-- If we did we would get a jumping effect as the player transitioned out of many into one.
-- The higher the camere influence is on a POI, the stronger we should pull the camera to that one.
if(currCameraTarget.pointsOfInterest ~= nil) then
-- let's get the total amount of camera influence being applied this frame.
for _,currPOI in pairs(currCameraTarget.pointsOfInterest) do
totalPointOfInterestInfluence = totalPointOfInterestInfluence + currPOI.cameraInfluence
end
-- Now get the average position and zoom based on the influence being applied by each POI.
for _,currPOI in pairs(currCameraTarget.pointsOfInterest) do
if(currPOI.enabled == true) then
pointOfInterestPos = currPOI.pos --[ryan] the center of the POI
-- This is the desired camera position determined by this POI.
newCamPos.x = (currPOI.pos.x - newCamPos.x) * currPOI.cameraInfluence + newCamPos.x
newCamPos.y = (currPOI.pos.y - newCamPos.y) * currPOI.cameraInfluence + newCamPos.y
-- we need to make sure we avoid a divide by 0 case. Just don't use POI influence if that happens.
if totalPointOfInterestInfluence == 0 then
avgCamPos.x = dummyCamPos.x
avgCamPos.y = dummyCamPos.y
else
avgCamPos.x = avgCamPos.x + (newCamPos.x * currPOI.cameraInfluence / totalPointOfInterestInfluence)
avgCamPos.y = avgCamPos.y + (newCamPos.y * currPOI.cameraInfluence / totalPointOfInterestInfluence)
end
-- We will start with the ambient zoom, and get an average of points of interest zooms
-- if the poi doesn't have a zoom set it should use the current zoom. A zoom value of -1 means it is not set.
if(currPOI.targetZoom == -1) then
currentPOIZoom = currentPOIZoom + CameraLib.newZoom
else
currentPOIZoom = currentPOIZoom + currPOI.percentageZoom
end
poiCount = poiCount + 1
end
end
end
-- If we have a cinematic camera set, we want to return smart camera control smoothly. We will interpolate from the cinematic camera
-- to the smart camera. A cameraSmoothedVal of 1 means the smart camera has complete control.
local cameraSmoothedVal = 1
if(toSmartCameraInterpolant < 1) then
toSmartCameraInterpolant = toSmartCameraInterpolant + toSmartCameraSpeed
cameraSmoothedVal = UtilLib.easeIn(toSmartCameraInterpolant)
end
local cameraZoomSmoothedVal = 1
if(toSmartCameraZoomInterpolant < 1) then
toSmartCameraZoomInterpolant = toSmartCameraZoomInterpolant + toSmartCameraZoomSpeed
cameraZoomSmoothedVal = UtilLib.easeIn(toSmartCameraZoomInterpolant)
end
-- if there are any points of interest, we need to find the desired zoom level.
if( poiCount > 0) then
newCamPos.x = avgCamPos.x
newCamPos.y = avgCamPos.y
pointOfInterestPercentage = totalPointOfInterestInfluence / poiCount
currentPOIZoom = currentPOIZoom / (poiCount)
CameraLib.interpolateCameraZoom(currentPOIZoom,toSmartCamZoomSpeed * cameraZoomSmoothedVal)
else
-- if we are leaving a point of interest, or the camera target has hit a zoom trigger, we need to approach that new zoom level
-- using an ease function.
if UtilLib.withinThreshold( getCameraZoom(),CameraLib.ambientZoom,0.1) == false then
CameraLib.interpolateCameraZoom(CameraLib.newZoom,toSmartCamZoomSpeed* cameraZoomSmoothedVal) --[ryan] if no POI's go to the ambient zoom
end
end
-- this is how we slowly move to the point of interest position, instead of snapping immediately to it.
local toDesiredPOIpercentage = (pointOfInterestPercentage - currPointOfInterestPercentage) * cameraMoveSpeed
currPointOfInterestPercentage = currPointOfInterestPercentage + toDesiredPOIpercentage
pointOfInterestPercentage = currPointOfInterestPercentage
--We have a desired velocity based offset, a desired right stick input offset, and a point of interest offset.
--We will be moving the camera to the average of these influences. The further the camera is from the desired target, the faster it will move.
local velocityInfluence = lStickCameraInfluence * pointOfInterestPercentage
smartCameraTarget:setPosition(targetPos.x + targetVel.x * velocityInfluence ,targetPos.y + targetVel.y * velocityInfluence,newCamPos.z)
smartCamPos = smartCameraTarget:getPosition()
local aimingInfluence = rStickCameraInfluence * pointOfInterestPercentage
rSmartCameraTarget:setPosition(targetPos.x + currRCamOffset.x * aimingInfluence,targetPos.y + currRCamOffset.y * aimingInfluence,newCamPos.z)
local rSmartCamPos = rSmartCameraTarget:getPosition()
--we are getting the average of all the stick inputs for the last 100 frames and choosing the average position
lastHead = averageSticks[stickHead]
totalSticks.x = totalSticks.x - lastHead.x + rStickX
totalSticks.y = totalSticks.y - lastHead.y + rStickY
averageSticks[stickHead].x = rStickX
averageSticks[stickHead].y = rStickY
stickHead = stickHead + 1 --instead of rewriting and adding up the entire array again, just update the last element
if( stickHead > numSticks) then
stickHead = 1
end
averageStick.x = totalSticks.x / numSticks
averageStick.y = totalSticks.y / numSticks
local toDesiredRCamPosX = (averageStick.x - currRCamOffset.x) * cameraMoveSpeed
local toDesiredRCamPosY = (averageStick.y - currRCamOffset.y) * cameraMoveSpeed
currRCamOffset.x = currRCamOffset.x + toDesiredRCamPosX
currRCamOffset.y = currRCamOffset.y + toDesiredRCamPosY
-- get the average position between the velocity target and the aiming target
if(useRightStickOffset == true) then
desiredStickPosition.x = (smartCamPos.x + rSmartCamPos.x) / 2
desiredStickPosition.y = (smartCamPos.y + rSmartCamPos.y) / 2
else
desiredStickPosition.x = (smartCamPos.x + targetPos.x) / 2
desiredStickPosition.y = (smartCamPos.y + targetPos.y) / 2
end
-- the vector to the player input target plus the vector to the desired point of interest spot
local toNewPosX = ((desiredStickPosition.x - dummyCamPos.x) * toPlayerInputSpeed) * (pointOfInterestPercentage) + (newCamPos.x - dummyCamPos.x) * cameraMoveSpeed
local toNewPosY = ((desiredStickPosition.y - dummyCamPos.y) * toPlayerInputSpeed) * (pointOfInterestPercentage) + (newCamPos.y - dummyCamPos.y) * cameraMoveSpeed
-- move the camera
dummyCameraTarget:setPosition(dummyCamPos.x + toNewPosX * cameraSmoothedVal, dummyCamPos.y + toNewPosY * cameraSmoothedVal, dummyCamPos.z)
-- if we are already returned to the smart camera, then check to see if we are within acceptable bounds, the "safe zone" of the screen.
if(toSmartCameraInterpolant >=1 ) then
local movedCamPos = dummyCameraTarget:getPosition()
-- let's do a border check here... if the ship has crossed the border we need to force camera movement.
local newX = movedCamPos.x
local newY = movedCamPos.y
local targetPos = currCameraTarget:getPosition()
local centerX, centerY = GameLib.screenToWorld( 0, 0 )
local maxWorldDeltaX, maxWorldDeltaY = GameLib.screenToWorld( safeZoneX, safeZoneY ) -- extents of the 'safe zone'
maxWorldDeltaX = math.abs(maxWorldDeltaX - centerX)
maxWorldDeltaY = math.abs(maxWorldDeltaY - centerY)
local worldDistY = newY - targetPos.y
local worldDistX = newX - targetPos.x
-- if the camera target is outside the safe zone, then force the camera to move into an acceptable position.
if worldDistY < -maxWorldDeltaY then
newY = targetPos.y - maxWorldDeltaY
end
if worldDistY > maxWorldDeltaY then
newY = targetPos.y + maxWorldDeltaY
end
if worldDistX < -maxWorldDeltaX then
newX = targetPos.x - maxWorldDeltaX
end
if worldDistX > maxWorldDeltaX then
newX = targetPos.x + maxWorldDeltaX
end
dummyCameraTarget:setPosition(newX, newY, newCamPos.z)
dummyCamPos = dummyCameraTarget:getPosition()
end
if(cameraDebugDraw == true) then
GameLib.drawDebugBox2D( desiredStickPosition.x -0.1, desiredStickPosition.y -0.1, desiredStickPosition.x+0.1, desiredStickPosition.y +0.1, 0, 1, 1 )
--where the ship is at
GameLib.drawDebugSphere2D(1,newCamPos.x,newCamPos.y,newCamPos.z,0,0,0)
--where the left stick is currently pointing
if(lStickX ~= nil) then
GameLib.drawDebugSphere2D(0.3,newCamPos.x + lStickX ,newCamPos.y + lStickY,newCamPos.z,1,1,0)
end
--where the right stick is currently pointing
GameLib.drawDebugSphere2D(0.3,newCamPos.x + rStickX ,newCamPos.y + rStickY,newCamPos.z,1,0,0)
if( pointOfInterestPos ~= nil) then
GameLib.drawDebugBox2D( pointOfInterestPos.x -0.4, pointOfInterestPos.y -0.4, pointOfInterestPos.x+0.4, pointOfInterestPos.y +0.4, 0.5, 0.4, 0.2 )
GameLib.drawDebugBox2D( newCamPos.x -0.1, newCamPos.y - 0.1, newCamPos.x + 0.1, newCamPos.y + 0.1, 0.5, 0.4, 0.2 )
GameLib.drawDebugLine(newCamPos.x,newCamPos.y,0,pointOfInterestPos.x,pointOfInterestPos.y,0, 0.5, 0.4, 0.2)
else
GameLib.drawDebugLine(dummyCamPos.x,dummyCamPos.y,0,dummyCamPos.x + toNewPosX,dummyCamPos.y + toNewPosY,0,1,0,0)
end
GameLib.drawDebugBox2D( newCamPos.x + averageStick.x -0.1, newCamPos.y + averageStick.y -0.1, newCamPos.x + averageStick.x+0.1, newCamPos.y + averageStick.y +0.1, 0, 1, 0 )
GameLib.drawDebugBox2D( newCamPos.x + currRCamOffset.x -0.1, newCamPos.y + currRCamOffset.y -0.1, newCamPos.x + currRCamOffset.x+0.1, newCamPos.y + currRCamOffset.y +0.1, 0.5, 0.5, 0 )
GameLib.drawDebugBox2D( smartCamPos.x -0.4, smartCamPos.y - 0.4, smartCamPos.x+ 0.4, smartCamPos.y +0.4, 1, 1, 0 )
GameLib.drawDebugBox2D( rSmartCamPos.x -0.4, rSmartCamPos.y - 0.4, rSmartCamPos.x+ 0.4, rSmartCamPos.y +0.4, 1, 0, 0 )
local left,top = GameLib.screenToWorld(256,144)
local right,bottom = GameLib.screenToWorld(1024,576)
local centerX,centerY = GameLib.screenToWorld(640,360)
GameLib.drawDebugBox2D( left,top, right, bottom , 1, 0, 0 )
end
end
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Enemy_Bridge : Enemy_Module {
//these are the modules that make up this enemy ship. They are hooked up in the editor.
public List<GameObject> leftEngines = new List<GameObject>();
public List<GameObject> rightEngines = new List<GameObject>();
public List<GameObject> forwardEngines = new List<GameObject>();
private GameObject playerBridge;
//how far can this ship see? When the player is in range, this enemy will take action.
public float visionRange = 60.0f;
private float forwardThreshold = 0.75f;
private float tooCloseDistance = 5.0f;
//Initialize the ship, and it's modules
public override void Start ()
{
base.Start();
AssignBridge(forwardEngines);
AssignBridge(rightEngines);
AssignBridge(leftEngines);
}
//the modules need to know about the bridge that is controlling it
void AssignBridge(List<GameObject> engineList)
{
foreach(GameObject engine in engineList)
{
engine.SendMessage("SetBridge",gameObject,SendMessageOptions.DontRequireReceiver);
}
}
//If this object gets a message to remove an engine, we can just quickly
//check each list to remove it since they are indexed by object id.
//Some engines exist in more than one list.
//The remove engine call will come from the engine module script when it is destroyed.
//removing it from this list prevents calling functions on a null object.
void RemoveEngine(GameObject engineToRemove)
{
forwardEngines.Remove(engineToRemove);
rightEngines.Remove(engineToRemove);
leftEngines.Remove(engineToRemove);
}
// Update is called once per frame
public override void Update () {
base.Update();
//The goal of this enemy is to destroy the player bridge. If we don't have one, find one.
//If a bridge doesn't exist, then it has been destroyed so take no more action.
if(playerBridge == null)
{
playerBridge = GameObject.FindWithTag("PlayerBridge");
return;
}
//if we have found a player bridge, check to see if it is within vision range.
float dist = Vector3.Distance(gameObject.transform.position,playerBridge.transform.position);
if(dist > visionRange)
{
return;
}
//Make two checks, one to see if the player is in front of the enemy
// and another to see which side of the enemy the player is on
Vector3 toBridge = Vector3.Normalize(playerBridge.transform.position - gameObject.transform.position);
float forwardCheck = Vector3.Dot(gameObject.transform.up, toBridge);
float sideCheck = Vector3.Dot(gameObject.transform.right, toBridge);
//if the player is in front of the ship and it is not too close, then move at it.
if(forwardCheck > forwardThreshold && dist > tooCloseDistance)
{
{
foreach( GameObject engine in forwardEngines)
{
if(engine)
engine.GetComponent<Enemy_Engine>().ActivateFromBridge();
}
}
}
else if(sideCheck > 0) // turn counter clockwise
{
foreach( GameObject engine in rightEngines)
{
if(engine)
engine.GetComponent<Enemy_Engine>().ActivateFromBridge();
}
}
else if(sideCheck <= 0) // turn clockwise
{
foreach( GameObject engine in leftEngines)
{
if(engine)
engine.GetComponent<Enemy_Engine>().ActivateFromBridge();
}
}
}
}