Whats up with instantiate?

Hi

This is a problem I noticed long ago but didnt said anything until now because it wasnt a problem for me when I noticed but Im dealing with it now. Instantiate does not instatiate the object in the position you want, instead it instantiates the object at 0,0,0 then moves(pretty slowly) the object to the desired position, normaly this isnt a problem but in multilevel platform game it is a big deal

The character is dead before it get “instantiated” in the desired position.

And here is a video of what happens…

Spawn code:

public void SpawnPlayer(){
        Instantiate(PlayerPrefab,  new Vector3 (SpawnPoints[CurrentSpawnPoint].transform.position.x,SpawnPoints[CurrentSpawnPoint].transform.position.y +1,SpawnPoints[CurrentSpawnPoint].transform.position.z), Quaternion.identity);
        Instantiate(GraphicsManager.Instance.SpawnParticles,  SpawnPoints[CurrentSpawnPoint].transform.position, Quaternion.identity);
    }

:(:(:(:frowning:

Instantiate doesn’t do anything like that. It instantiates an object where you say, and that’s it, the function is completed and done with. If the object moves, something else is moving it.

–Eric

Perhaps your character is still in the dead state when it is instantiated

There is nothing moving the character (at least not in my code), the bit of code I posted is all I use to Spawn the character.

I use the 2D character controller by Prime31 but I checked it and couldnt see anything that would move the character on Awake or Start, but here is:

#define DEBUG_CC2D_RAYS
using UnityEngine;
using System;
using System.Collections.Generic;


[RequireComponent( typeof( BoxCollider2D ), typeof( Rigidbody2D ) )]
public class CharacterController2D : MonoBehaviour
{
    #region internal types

    private struct CharacterRaycastOrigins
    {
        public Vector3 topRight;
        public Vector3 topLeft;
        public Vector3 bottomRight;
        public Vector3 bottomLeft;
    }

    public class CharacterCollisionState2D
    {
        public bool right;
        public bool left;
        public bool above;
        public bool below;
        public bool becameGroundedThisFrame;
        public bool movingDownSlope;
        public float slopeAngle;


        public bool hasCollision()
        {
            return below || right || left || above;
        }


        public void reset()
        {
            right = left = above = below = becameGroundedThisFrame = movingDownSlope = false;
            slopeAngle = 0f;
        }


        public override string ToString()
        {
            return string.Format( "[CharacterCollisionState2D] r: {0}, l: {1}, a: {2}, b: {3}, movingDownSlope: {4}, angle: {5}",
                                 right, left, above, below, movingDownSlope, slopeAngle );
        }
    }

    #endregion


    #region events, properties and fields

    public event Action<RaycastHit2D> onControllerCollidedEvent;
    public event Action<Collider2D> onTriggerEnterEvent;
    public event Action<Collider2D> onTriggerStayEvent;
    public event Action<Collider2D> onTriggerExitEvent;


    /// <summary>
    /// toggles if the RigidBody2D methods should be used for movement or if Transform.Translate will be used. All the usual Unity rules for physics based movement apply when true
    /// such as getting your input in Update and only calling move in FixedUpdate amonst others.
    /// </summary>
    public bool usePhysicsForMovement = false;

    [SerializeField]
    [Range( 0.001f, 0.3f )]
    private float _skinWidth = 0.02f;

    /// <summary>
    /// defines how far in from the edges of the collider rays are cast from. If cast with a 0 extent it will often result in ray hits that are
    /// not desired (for example a foot collider casting horizontally from directly on the surface can result in a hit)
    /// </summary>
    public float skinWidth
    {
        get { return _skinWidth; }
        set
        {
            _skinWidth = value;
            recalculateDistanceBetweenRays();
        }
    }


    /// <summary>
    /// mask with all layers that the player should interact with
    /// </summary>
    public LayerMask platformMask = 0;

    /// <summary>
    /// mask with all layers that should act as one-way platforms. Note that one-way platforms should always be EdgeCollider2Ds. This is private because it does not support being
    /// updated anytime outside of the inspector for now.
    /// </summary>
    [SerializeField]
    private LayerMask oneWayPlatformMask = 0;


    /// <summary>
    /// the max slope angle that the CC2D can climb
    /// </summary>
    /// <value>The slope limit.</value>
    [Range( 0, 90f )]
    public float slopeLimit = 30f;


    /// <summary>
    /// curve for multiplying speed based on slope (negative = down slope and positive = up slope)
    /// </summary>
    public AnimationCurve slopeSpeedMultiplier = new AnimationCurve( new Keyframe( -90, 1.5f ), new Keyframe( 0, 1 ), new Keyframe( 90, 0 ) );

    [Range( 2, 20 )]
    public int totalHorizontalRays = 8;
    [Range( 2, 20 )]
    public int totalVerticalRays = 4;


    /// <summary>
    /// this is used to calculate the downward ray that is cast to check for slopes. We use the somewhat arbitray value 75 degrees
    /// to calcuate the lenght of the ray that checks for slopes.
    /// </summary>
    private float _slopeLimitTangent = Mathf.Tan( 75f * Mathf.Deg2Rad );


    /// <summary>
    /// if true, a new GameObject named CC2DTriggerHelper will be created in Awake and latched on via a DistanceJoint2D
    /// to the player so that trigger messages can be received
    /// </summary>
    public bool createTriggerHelperGameObject = false;

    [Range( 0.8f, 0.999f )]
    public float triggerHelperBoxColliderScale = 0.95f;


    [HideInInspector]
    public new Transform transform;
    [HideInInspector]
    public BoxCollider2D boxCollider;
    [HideInInspector]
    public Rigidbody2D rigidBody2D;

    [HideInInspector]
    [NonSerialized]
    public CharacterCollisionState2D collisionState = new CharacterCollisionState2D();
    [HideInInspector]
    [NonSerialized]
    public Vector3 velocity;
    public bool isGrounded { get { return collisionState.below; } }

    private const float kSkinWidthFloatFudgeFactor = 0.001f;

    #endregion


    /// <summary>
    /// holder for our raycast origin corners (TR, TL, BR, BL)
    /// </summary>
    private CharacterRaycastOrigins _raycastOrigins;

    /// <summary>
    /// stores our raycast hit during movement
    /// </summary>
    private RaycastHit2D _raycastHit;

    /// <summary>
    /// stores any raycast hits that occur this frame. we have to store them in case we get a hit moving
    /// horizontally and vertically so that we can send the events after all collision state is set
    /// </summary>
    private List<RaycastHit2D> _raycastHitsThisFrame = new List<RaycastHit2D>( 2 );

    // horizontal/vertical movement data
    private float _verticalDistanceBetweenRays;
    private float _horizontalDistanceBetweenRays;


    #region Monobehaviour

    void Awake()
    {
        // add our one-way platforms to our normal platform mask so that we can land on them from above
        platformMask |= oneWayPlatformMask;

        // cache some components
        transform = GetComponent<Transform>();
        boxCollider = GetComponent<BoxCollider2D>();
        rigidBody2D = GetComponent<Rigidbody2D>();

        // we dont have a bounds property until Unity 4.5+ so we really don't need the BoxCollider2D to be active since we just use
        // it for it's size and center properties so might as well remove it from play
        boxCollider.enabled = false;

        if( createTriggerHelperGameObject )
            createTriggerHelper();

        // here, we trigger our properties that have setters with bodies
        skinWidth = _skinWidth;
    }


    public void OnTriggerEnter2D( Collider2D col )
    {
        if( onTriggerEnterEvent != null )
            onTriggerEnterEvent( col );
    }


    public void OnTriggerStay2D( Collider2D col )
    {
        if( onTriggerStayEvent != null )
            onTriggerStayEvent( col );
    }


    public void OnTriggerExit2D( Collider2D col )
    {
        if( onTriggerExitEvent != null )
            onTriggerExitEvent( col );
    }

    #endregion


    [System.Diagnostics.Conditional( "DEBUG_CC2D_RAYS" )]
    private void DrawRay( Vector3 start, Vector3 dir, Color color )
    {
        Debug.DrawRay( start, dir, color );
    }


    #region Public

    public void move( Vector3 deltaMovement )
    {
        // save off our current grounded state
        var wasGroundedBeforeMoving = collisionState.below;

        // clear our state
        collisionState.reset();
        _raycastHitsThisFrame.Clear();

        primeRaycastOrigins();


        // first, we check for a slope below us before moving
        // only check slopes if we are going down and grounded
        if( deltaMovement.y < 0 && wasGroundedBeforeMoving )
            handleVerticalSlope( ref deltaMovement );

        // now we check movement in the horizontal dir
        if( deltaMovement.x != 0 )
            moveHorizontally( ref deltaMovement );

        // next, check movement in the vertical dir
        if( deltaMovement.y != 0 )
            moveVertically( ref deltaMovement );


        // move then update our state
        if( usePhysicsForMovement )
        {
#if UNITY_4_5 || UNITY_4_6
            rigidbody2D.MovePosition( transform.position + deltaMovement );
#else
            GetComponent<Rigidbody2D>().velocity = deltaMovement / Time.fixedDeltaTime;
#endif
            velocity = GetComponent<Rigidbody2D>().velocity;
        }
        else
        {
            transform.Translate( deltaMovement, Space.World );
         
            // only calculate velocity if we have a non-zero deltaTime
            if( Time.deltaTime > 0 )
                velocity = deltaMovement / Time.deltaTime;
        }

        // set our becameGrounded state based on the previous and current collision state
        if( !wasGroundedBeforeMoving && collisionState.below )
            collisionState.becameGroundedThisFrame = true;

        // send off the collision events if we have a listener
        if( onControllerCollidedEvent != null )
        {
            for( var i = 0; i < _raycastHitsThisFrame.Count; i++ )
                onControllerCollidedEvent( _raycastHitsThisFrame[i] );
        }
    }


    /// <summary>
    /// this should be called anytime you have to modify the BoxCollider2D at runtime. It will recalculate the distance between the rays used for collision detection.
    /// It is also used in the skinWidth setter in case it is changed at runtime.
    /// </summary>
    public void recalculateDistanceBetweenRays()
    {
        // figure out the distance between our rays in both directions
        // horizontal
        var colliderUseableHeight = boxCollider.size.y * Mathf.Abs( transform.localScale.y ) - ( 2f * _skinWidth );
        _verticalDistanceBetweenRays = colliderUseableHeight / ( totalHorizontalRays - 1 );

        // vertical
        var colliderUseableWidth = boxCollider.size.x * Mathf.Abs( transform.localScale.x ) - ( 2f * _skinWidth );
        _horizontalDistanceBetweenRays = colliderUseableWidth / ( totalVerticalRays - 1 );
    }


    /// <summary>
    /// this is called internally if createTriggerHelperGameObject is true. It is provided as a public method
    /// in case you want to grab a handle on the GO created to modify it in some way. Note that by default only
    /// collisions with triggers will be allowed to pass through and fire the events.
    /// </summary>
    public GameObject createTriggerHelper()
    {
        var go = new GameObject( "PlayerTriggerHelper" );
        go.hideFlags = HideFlags.HideInHierarchy;
        go.layer = gameObject.layer;
        // scale is slightly less so that we don't get trigger messages when colliding with non-triggers
        go.transform.localScale = transform.localScale * triggerHelperBoxColliderScale;

        go.AddComponent<CC2DTriggerHelper>().setParentCharacterController( this );

        var rb = go.AddComponent<Rigidbody2D>();
        rb.mass = 0f;
        rb.gravityScale = 0f;

        var bc = go.AddComponent<BoxCollider2D>();
        bc.size = boxCollider.size;
        bc.isTrigger = true;

        var joint = go.AddComponent<DistanceJoint2D>();
        joint.connectedBody = GetComponent<Rigidbody2D>();
        joint.distance = 0f;

        return go;
    }

    #endregion


    #region Private Movement Methods

    /// <summary>
    /// resets the raycastOrigins to the current extents of the box collider inset by the skinWidth. It is inset
    /// to avoid casting a ray from a position directly touching another collider which results in wonky normal data.
    /// </summary>
    private void primeRaycastOrigins()
    {
        var scaledColliderSize = new Vector2( boxCollider.size.x * Mathf.Abs( transform.localScale.x ), boxCollider.size.y * Mathf.Abs( transform.localScale.y ) ) / 2;
        var scaledCenter = new Vector2( boxCollider.offset.x * transform.localScale.x, boxCollider.offset.y * transform.localScale.y );

        _raycastOrigins.topRight = transform.position + new Vector3( scaledCenter.x + scaledColliderSize.x, scaledCenter.y + scaledColliderSize.y );
        _raycastOrigins.topRight.x -= _skinWidth;
        _raycastOrigins.topRight.y -= _skinWidth;

        _raycastOrigins.topLeft = transform.position + new Vector3( scaledCenter.x - scaledColliderSize.x, scaledCenter.y + scaledColliderSize.y );
        _raycastOrigins.topLeft.x += _skinWidth;
        _raycastOrigins.topLeft.y -= _skinWidth;

        _raycastOrigins.bottomRight = transform.position + new Vector3( scaledCenter.x + scaledColliderSize.x, scaledCenter.y -scaledColliderSize.y );
        _raycastOrigins.bottomRight.x -= _skinWidth;
        _raycastOrigins.bottomRight.y += _skinWidth;

        _raycastOrigins.bottomLeft = transform.position + new Vector3( scaledCenter.x - scaledColliderSize.x, scaledCenter.y -scaledColliderSize.y );
        _raycastOrigins.bottomLeft.x += _skinWidth;
        _raycastOrigins.bottomLeft.y += _skinWidth;
    }


    /// <summary>
    /// we have to use a bit of trickery in this one. The rays must be cast from a small distance inside of our
    /// collider (skinWidth) to avoid zero distance rays which will get the wrong normal. Because of this small offset
    /// we have to increase the ray distance skinWidth then remember to remove skinWidth from deltaMovement before
    /// actually moving the player
    /// </summary>
    private void moveHorizontally( ref Vector3 deltaMovement )
    {
        var isGoingRight = deltaMovement.x > 0;
        var rayDistance = Mathf.Abs( deltaMovement.x ) + _skinWidth;
        var rayDirection = isGoingRight ? Vector2.right : -Vector2.right;
        var initialRayOrigin = isGoingRight ? _raycastOrigins.bottomRight : _raycastOrigins.bottomLeft;

        for( var i = 0; i < totalHorizontalRays; i++ )
        {
            var ray = new Vector2( initialRayOrigin.x, initialRayOrigin.y + i * _verticalDistanceBetweenRays );

            DrawRay( ray, rayDirection * rayDistance, Color.red );
            _raycastHit = Physics2D.Raycast( ray, rayDirection, rayDistance, platformMask & ~oneWayPlatformMask );
            if( _raycastHit )
            {
                // the bottom ray can hit slopes but no other ray can so we have special handling for those cases
                if( i == 0 && handleHorizontalSlope( ref deltaMovement, Vector2.Angle( _raycastHit.normal, Vector2.up ), isGoingRight ) )
                {
                    _raycastHitsThisFrame.Add( _raycastHit );
                    break;
                }

                // set our new deltaMovement and recalculate the rayDistance taking it into account
                deltaMovement.x = _raycastHit.point.x - ray.x;
                rayDistance = Mathf.Abs( deltaMovement.x );

                // remember to remove the skinWidth from our deltaMovement
                if( isGoingRight )
                {
                    deltaMovement.x -= _skinWidth;
                    collisionState.right = true;
                }
                else
                {
                    deltaMovement.x += _skinWidth;
                    collisionState.left = true;
                }

                _raycastHitsThisFrame.Add( _raycastHit );

                // we add a small fudge factor for the float operations here. if our rayDistance is smaller
                // than the width + fudge bail out because we have a direct impact
                if( rayDistance < _skinWidth + kSkinWidthFloatFudgeFactor )
                    break;
            }
        }
    }


    private bool handleHorizontalSlope( ref Vector3 deltaMovement, float angle, bool isGoingRight )
    {
        //Debug.Log( "angle: " + angle );
        // disregard 90 degree angles (walls)
        if( Mathf.RoundToInt( angle ) == 90 )
            return false;

        // if we can walk on slopes and our angle is small enough we need to move up
        if( angle < slopeLimit )
        {
            // we only need to adjust the y movement if we are not jumping
            // TODO: this uses a magic number which isn't ideal!
            if( deltaMovement.y < 0.07f )
            {
                // apply the slopeModifier to slow our movement up the slope
                var slopeModifier = slopeSpeedMultiplier.Evaluate( angle );
                deltaMovement.x *= slopeModifier;

                // we dont set collisions on the sides for this since a slope is not technically a side collision
                if( isGoingRight )
                {
                    deltaMovement.x -= _skinWidth;
                }
                else
                {
                    deltaMovement.x += _skinWidth;
                }

                // smooth y movement when we climb. we make the y movement equivalent to the actual y location that corresponds
                // to our new x location using our good friend Pythagoras
                deltaMovement.y = Mathf.Abs( Mathf.Tan( angle * Mathf.Deg2Rad ) * deltaMovement.x );

                collisionState.below = true;
            }
        }
        else // too steep. get out of here
        {
            Debug.Log( "TOO STEEP" );
            deltaMovement.x = 0;
        }

        return true;
    }


    private void moveVertically( ref Vector3 deltaMovement )
    {
        var isGoingUp = deltaMovement.y > 0;
        var rayDistance = Mathf.Abs( deltaMovement.y ) + _skinWidth;
        var rayDirection = isGoingUp ? Vector2.up : -Vector2.up;
        var initialRayOrigin = isGoingUp ? _raycastOrigins.topLeft : _raycastOrigins.bottomLeft;

        // apply our horizontal deltaMovement here so that we do our raycast from the actual position we would be in if we had moved
        initialRayOrigin.x += deltaMovement.x;

        // if we are moving up, we should ignore the layers in oneWayPlatformMask
        var mask = platformMask;
        if( isGoingUp )
            mask &= ~oneWayPlatformMask;

        for( var i = 0; i < totalVerticalRays; i++ )
        {
            var ray = new Vector2( initialRayOrigin.x + i * _horizontalDistanceBetweenRays, initialRayOrigin.y );

            DrawRay( ray, rayDirection * rayDistance, Color.red );
            _raycastHit = Physics2D.Raycast( ray, rayDirection, rayDistance, mask );
            if( _raycastHit )
            {
                // set our new deltaMovement and recalculate the rayDistance taking it into account
                deltaMovement.y = _raycastHit.point.y - ray.y;
                rayDistance = Mathf.Abs( deltaMovement.y );

                // remember to remove the skinWidth from our deltaMovement
                if( isGoingUp )
                {
                    deltaMovement.y -= _skinWidth;
                    collisionState.above = true;
                }
                else
                {
                    deltaMovement.y += _skinWidth;
                    collisionState.below = true;
                }

                _raycastHitsThisFrame.Add( _raycastHit );

                // we add a small fudge factor for the float operations here. if our rayDistance is smaller
                // than the width + fudge bail out because we have a direct impact
                if( rayDistance < _skinWidth + kSkinWidthFloatFudgeFactor )
                    return;
            }
        }
    }


    /// <summary>
    /// checks the center point under the BoxCollider2D for a slope. If it finds one then the deltaMovement is adjusted so that
    /// the player stays grounded and the slopeSpeedModifier is taken into account to speed up movement.
    /// </summary>
    /// <param name="deltaMovement">Delta movement.</param>
    private void handleVerticalSlope( ref Vector3 deltaMovement )
    {
        // slope check from the center of our collider
        var centerOfCollider = ( _raycastOrigins.bottomLeft.x + _raycastOrigins.bottomRight.x ) * 0.5f;
        var rayDirection = -Vector2.up;

        // the ray distance is based on our slopeLimit
        var slopeCheckRayDistance = _slopeLimitTangent * ( _raycastOrigins.bottomRight.x - centerOfCollider );

        var slopeRay = new Vector2( centerOfCollider, _raycastOrigins.bottomLeft.y );
        DrawRay( slopeRay, rayDirection * slopeCheckRayDistance, Color.yellow );
        _raycastHit = Physics2D.Raycast( slopeRay, rayDirection, slopeCheckRayDistance, platformMask );
        if( _raycastHit )
        {
            // bail out if we have no slope
            var angle = Vector2.Angle( _raycastHit.normal, Vector2.up );
            if( angle == 0 )
                return;

            // we are moving down the slope if our normal and movement direction are in the same x direction
            var isMovingDownSlope = Mathf.Sign( _raycastHit.normal.x ) == Mathf.Sign( deltaMovement.x );
            if( isMovingDownSlope )
            {
                // going down we want to speed up in most cases so the slopeSpeedMultiplier curve should be > 1 for negative angles
                var slopeModifier = slopeSpeedMultiplier.Evaluate( -angle );
                deltaMovement.y = _raycastHit.point.y - slopeRay.y;
                deltaMovement.x *= slopeModifier;
                collisionState.movingDownSlope = true;
                collisionState.slopeAngle = angle;
            }
        }
    }

    #endregion


}

But anyways, how would that work? how is anything moving the character to 0,0,0 BEFORE instantiating it where is supposed to be? wouldnt that be impossible?

No, thats not the case.

As far as I have ever known Instantiate asks you where you want it to be… instantiated at? I mean at least I have never noticed a behavior like this. Maybe SpawnPlayer() is being called rapidly over and over? What happens when you stick a Debug.Log(“Blah”); into that method?

No, thats not what happening, but anyways I uploaded a video of normal behaviour, its the same level but this check point is at the side not below the spikes, as you can see it works fine.

https://www.youtube.com/watch?v=UPM0C2Wywc8

I see. That does seem strange, but without seeing the way things are working I couldn’t tell you what to try!

What does the killing in your code? Do the spikes have an OnCollisionEnter method? Did you change the CollisionDetectionMode to Continuous?

No, the spikes have no code at all just a tag, the controller manages everything.

Perhaps you’ve tried this already but can you put a breakpoint on line number 3 in this code block and attach the debugger. Immediately after the PlayerPrefab is instantiated, check its transform and its state.

public void SpawnPlayer(){
        Instantiate(PlayerPrefab,  new Vector3 (SpawnPoints[CurrentSpawnPoint].transform.position.x,SpawnPoints[CurrentSpawnPoint].transform.position.y +1,SpawnPoints[CurrentSpawnPoint].transform.position.z), Quaternion.identity);
        Instantiate(GraphicsManager.Instance.SpawnParticles,  SpawnPoints[CurrentSpawnPoint].transform.position, Quaternion.identity);
    }

It would go a long way to verify what you are saying to post a screenshot of the instantiated object’s position in the debugger immediately after this line.

I dont know how to do that actually… :sweat_smile:

Sorry, I’m not at my computer at the moment. I’ll try to post some instructions later if you’re still stuck. I’m sure you can find some tutorials on how to do this. It will help you a lot so I highly recommend taking the time to learn to use the debugger.

As promised,

Open your script in MonoDevelop and move your cursor to the correct line. Press F9 to set a breakpoint here.
Alternatively, click on the gray bar to the left of the text editor. You will know if a breakpoint was set if a red circle appears and your line of code turns red.

Attach the IDE debugger to the Unity Editor by pressing the Play key in the top left corner. In the dialog that appears, choose Unity Editor. Press the Attach button

Start your game. When the computer gets to the line with the break point, it will pause. The Unity Editor will stop and the Mono Develop IDE should flash, or otherwise take focus. The line of code with the break point is now yellow, indicating it is about to be executed.

At this point, you can inspect the state of the game. In my own tests, I found that the object created by Instantiate in fact starts in precisely the location I told it to. It does not seem to move as you suggested.

You can also step through your code line by line, which can be an extremely effective way to find difficult bugs. Learn the following commands (and memorize the hot keys), they will help a lot:

Step Over (F10): execute the current line but do not show all the steps as part of executing it. If I call a function, simply evaluate it, give me the result and then pause before moving on.

Step Into (F11): show exactly what the next step is. If I call a function, jump to the first line of code in that function and pause.

Step Out Of (Shift + F11): execute until you pop the current level of the stack, then pause. In other words, finish the current function, return its value and then pause.

Continue (F5): go back to executing normally until you hit another break point.

2 Likes

I dont know how prime31 character controller works, but there seems to be some sort of delta movement system handled with move function:
publicvoid move(Vector3 deltaMovement )

As that function is not called in this CharacterController2D script, it must be called somewhere else. I suppose in InputControl or something like that? I’m sure there is a variable that holds the position for previous frame, and calculates delta from that (and current position)

Perhaps where ever that move() function is called, variable that holds previous frame position resets to 0,0,0 position or perhaps it holds the position where you died and as deltamovement systems usually interpolates movement between two points with some sort of max velocity, your player is positioned to 0,0,0 + maxvel (or probably postition where player died), and poor bastard dies again when there happens to be spikes between, repeatedly. If that is the case, you probably need to update that variable (that holds last pos) with the position you use when instantiating player after dying.