I’m new here so HI BTW
I want to do something like this:
Player starts the game with the control of some cute character. During the game he can press R to change focus and control from the Cute Character to a cool looking spaceship. The later he can press R again and he will be controlling the Character again.
I used some scripts and all of the assets from 2d platformer tutorial. I set up key: ‘R’ in Project Settings → Input. Then I wrote a function handling R the way I want (described above). It’s working, but many times when You press R a screen goes crazy, camera is working strange. Besides Controllable State even comes back to the original object. Well. Better if You would see it: http://samur.pl/test/platf2.html
I thought, I had to add some delay, so I found yield. I’ve started Coroutine, etc, but still it’s working just the same. Not the way I want it to.
Here I past my Update script that is handling R button and main game loop.
// An internal reference to the attached CameraScrolling script
private var cameraScrolling : CameraScrolling;
// Who is the player controlling
private var selected = 0;
// List of objects to control
var targets : Transform[];
// What to display on the buttons in the window
var targetButtonNames : String[];
// On start up, we send the SetControllable () message to turn the different players on and off.
function Awake () {
// Get the reference to our CameraScrolling script attached to this camera;
cameraScrolling = GetComponent (CameraScrolling);
// Set the scrolling camera's target to be our character at the start.
cameraScrolling.SetTarget (targets[0], true);
}
var buttonActive : boolean;
var message = "";
function Update () {
onHandleRButton ();
}
private var windowRect = Rect (20, 20, 250, 50);
function onHandleRButton () {
StartCoroutine ("handleRButton");
}
function activeChar () {
targets[1].gameObject.SendMessage ("SetControllable", false, SendMessageOptions.DontRequireReceiver);
targets[0].gameObject.SendMessage ("SetControllable", true, SendMessageOptions.DontRequireReceiver);
yield;
cameraScrolling.SetTarget (targets[0], true);
}
function activeShip () {
targets[0].gameObject.SendMessage ("SetControllable", false, SendMessageOptions.DontRequireReceiver);
targets[1].gameObject.SendMessage ("SetControllable", true, SendMessageOptions.DontRequireReceiver);
yield;
cameraScrolling.SetTarget (targets[1], true);
}
function handleRButton () {
var buttonPressed = Input.GetButton ("R");
if ( buttonPressed )
{
if ( buttonActive )
{
// Button was activated
message = "Unpressed";
// Set Character active
activeChar();
buttonActive = false;
} else {
// Button was deactivated
message = "Pressed";
// Set SpaceShip active
activeShip ();
buttonActive = true;
}
print( message );
}
}
@script RequireComponent (CameraScrolling)
Here you have a CameraScrolling script. It was with the tutorial.
// The object in our scene that our camera is currently tracking.
private var target : Transform;
// How far back should the camera be from the target?
var distance = 15.0;
// How strict should the camera follow the target? Lower values make the camera more lazy.
var springiness = 4.0;
// Keep handy reference sto our level's attributes. We set up these references in the Awake () function.
// This also is very slightly more performant, but it's mostly just convenient.
private var levelAttributes : LevelAttributes;
private var levelBounds : Rect;
private var targetLock = false;
// This is for setting interpolation on our target, but making sure we don't permanently
// alter the target's interpolation setting. This is used in the SetTarget () function.
private var savedInterpolationSetting = RigidbodyInterpolation.None;
function Awake () {
// Set up our convenience references.
levelAttributes = LevelAttributes.GetInstance ();
levelBounds = levelAttributes.bounds;
}
function SetTarget (newTarget : Transform, snap : boolean) {
// If there was a target, reset its interpolation value if it had a rigidbody.
if (target) {
// Reset the old target's interpolation back to the saved value.
targetRigidbody = target.GetComponent (Rigidbody);
if (targetRigidbody)
targetRigidbody.interpolation = savedInterpolationSetting;
}
// Set our current target to be the value passed to SetTarget ()
target = newTarget;
// Now, save the new target's interpolation setting and set it to interpolate for now.
// This will make our camera move more smoothly. Only do this if we didn't set the
// target to null (nothing).
if (target) {
targetRigidbody = target.GetComponent (Rigidbody);
if (targetRigidbody) {
savedInterpolationSetting = targetRigidbody.interpolation;
targetRigidbody.interpolation = RigidbodyInterpolation.Interpolate;
}
}
// If we should snap the camera to the target, do so now.
// Otherwise, the camera's position will change in the LateUpdate () function.
if (snap) {
transform.position = GetGoalPosition ();
}
}
// Provide another version of SetTarget that doesn't require the snap variable to set.
// This is for convenience and cleanliness. By default, we will not snap to the target.
function SetTarget (newTarget : Transform) {
SetTarget (newTarget, false);
}
// This is a simple accessor function, sometimes called a "getter". It is a publically callable
// function that returns a private variable. Notice how target defined at the top of the script
// is marked "private"? We can not access it from other scripts directly. Therefore, we just
// have a function that returns it. Sneaky!
function GetTarget () {
return target;
}
// You almost always want camera motion to go inside of LateUpdate (), so that the camera follows
// the target _after_ it has moved. Otherwise, the camera may lag one frame behind.
function LateUpdate () {
// Where should our camera be looking right now?
var goalPosition = GetGoalPosition ();
// Interpolate between the current camera position and the goal position.
// See the documentation on Vector3.Lerp () for more information.
transform.position = Vector3.Lerp (transform.position, goalPosition, Time.deltaTime * springiness);
}
// Based on the camera attributes and the target's special camera attributes, find out where the
// camera should move to.
function GetGoalPosition () {
// If there is no target, don't move the camera. So return the camera's current position as the goal position.
if (!target)
return transform.position;
// Our camera script can take attributes from the target. If there are no attributes attached, we have
// the following defaults.
// How high in world space should the camera look above the target?
var heightOffset = 0.0;
// How much should we zoom the camera based on this target?
var distanceModifier = 1.0;
// By default, we won't account for any target velocity in our calculations;
var velocityLookAhead = 0.0;
var maxLookAhead = Vector2 (0.0, 0.0);
// Look for CameraTargetAttributes in our target.
var cameraTargetAttributes = target.GetComponent (CameraTargetAttributes);
// If our target has special attributes, use these instead of our above defaults.
if (cameraTargetAttributes) {
heightOffset = cameraTargetAttributes.heightOffset;
distanceModifier = cameraTargetAttributes.distanceModifier;
velocityLookAhead = cameraTargetAttributes.velocityLookAhead;
maxLookAhead = cameraTargetAttributes.maxLookAhead;
}
// First do a rough goalPosition that simply follows the target at a certain relative height and distance.
var goalPosition = target.position + Vector3 (0, heightOffset, -distance * distanceModifier);
// Next, we refine our goalPosition by taking into account our target's current velocity.
// This will make the camera slightly look ahead to wherever the character is going.
// First assume there is no velocity.
// This is so if the camera's target is not a Rigidbody, it won't do any look-ahead calculations because everything will be zero.
var targetVelocity = Vector3.zero;
// If we find a Rigidbody on the target, that means we can access a velocity!
var targetRigidbody = target.GetComponent (Rigidbody);
if (targetRigidbody)
targetVelocity = targetRigidbody.velocity;
// If we find a PlatformerController on the target, we can access a velocity from that!
targetPlatformerController = target.GetComponent (PlatformerController);
if (targetPlatformerController)
targetVelocity = targetPlatformerController.GetVelocity ();
// If you've had a physics class, you may recall an equation similar to: position = velocity * time;
// Here we estimate what the target's position will be in velocityLookAhead seconds.
var lookAhead = targetVelocity * velocityLookAhead;
// We clamp the lookAhead vector to some sane values so that the target doesn't go offscreen.
// This calculation could be more advanced (lengthy), taking into account the target's viewport position,
// but this works pretty well in practice.
lookAhead.x = Mathf.Clamp (lookAhead.x, -maxLookAhead.x, maxLookAhead.x);
lookAhead.y = Mathf.Clamp (lookAhead.y, -maxLookAhead.y, maxLookAhead.y);
// We never want to take z velocity into account as this is 2D. Just make sure it's zero.
lookAhead.z = 0.0;
// Now add in our lookAhead calculation. Our camera following is now a bit better!
goalPosition += lookAhead;
// To put the icing on the cake, we will make so the positions beyond the level boundaries
// are never seen. This gives your level a great contained feeling, with a definite beginning
// and ending.
var clampOffset = Vector3.zero;
// Temporarily set the camera to the goal position so we can test positions for clamping.
// But first, save the previous position.
var cameraPositionSave = transform.position;
transform.position = goalPosition;
// Get the target position in viewport space. Viewport space is relative to the camera.
// The bottom left is (0,0) and the upper right is (1,1)
// @TODO Viewport space changing in Unity 2.0?
var targetViewportPosition = camera.WorldToViewportPoint (target.position);
// First clamp to the right and top. After this we will clamp to the bottom and left, so it will override this
// clamping if it needs to. This only occurs if your level is really small so that the camera sees more than
// the entire level at once.
// What is the world position of the very upper right corner of the camera?
var upperRightCameraInWorld = camera.ViewportToWorldPoint (Vector3 (1.0, 1.0, targetViewportPosition.z));
// Find out how far outside the world the camera is right now.
clampOffset.x = Mathf.Min (levelBounds.xMax - upperRightCameraInWorld.x, 0.0);
clampOffset.y = Mathf.Min ((levelBounds.yMax - upperRightCameraInWorld.y), 0.0);
// Now we apply our clamping to our goalPosition. Now our camera won't go past the right and top boundaries of the level!
goalPosition += clampOffset;
// Now we do basically the same thing, except clamp to the lower left of the level. This will override any previous clamping
// if the level is really small. That way you'll for sure never see past the lower-left of the level, but if the camera is
// zoomed out too far for the level size, you will see past the right or top of the level.
transform.position = goalPosition;
var lowerLeftCameraInWorld = camera.ViewportToWorldPoint (Vector3 (0.0, 0.0, targetViewportPosition.z));
// Find out how far outside the world the camera is right now.
clampOffset.x = Mathf.Max ((levelBounds.xMin - lowerLeftCameraInWorld.x), 0.0);
clampOffset.y = Mathf.Max ((levelBounds.yMin - lowerLeftCameraInWorld.y), 0.0);
// Now we apply our clamping to our goalPosition once again. Now our camera won't go past the left and bottom boundaries of the level!
goalPosition += clampOffset;
// Now that we're done calling functions on the camera, we can set the position back to the saved position;
transform.position = cameraPositionSave;
// Send back our spiffily calculated goalPosition back to the caller!
return goalPosition;
}
Maybe someone can help? Thanks!