0
\$\begingroup\$

I've been stuck with this nightmare for a while, and it's certainly my perfectionism. What I have is a cannon that fires a shell from an object pool. The order of operations is as follows.

  1. The disabled pooled object is SetActive
  2. The pooled Object's forward vector is set to match the cannon's forward vector (previously also used rotations)
  3. The pooled Object's position is set to the tip of the barrel (previously from another transform that was placed at the tip of the barrel, now done via a field, this caused 0 difference in the issue)
  4. The pooled Object's velocity is set based on its now changed forward vector.

After all this is done, the shell is supposed to be perfectly accurate. Instead, the shell appears to come into existence offset from the barrel tip, when a Gizmo given the same transform has no such issue. The shell's trajectory is also incorrect, as the barrel's forward vector and the shell's forward vector deviate before the shell even has its first frame of flight. I have attached a screenshot of the issue, and the hierarchy of objects which shows where the cannon (which gives the shell its position) is.

It should also be noted that, for some reason, this problem is less severe (but still present) when the game is built, and is far worse in editor.

Shell position deviates from barrel tip (yellow sphere) when fired

Hierarchy showing the position of the cannon which gives the shell its position

Another very important note is that the pivot point of the shell, and the mesh center, are in fact in the center of the shell. I double checked this by instantiating primitive spheres in the position and that creates an identical result.

Here is a (simplified) version of the code which creates and fires the shell.

Code is applied relative to the hierarchy as follows:

Player (parent of PlayerVehicle, not pictured) contains PlayerController

Player Vehicle contains a rigidbody

120mm_Mid_Long contains CannonBehavior

Shell (not pictured, no parent in hierarchy) contains ShellBasic and ShellPhysics

PlayerController : MonoBehaviour
{
    Player player;
    public KeyCode unlockMouse = KeyCode.LeftAlt;

    void Start()
    {
        this.player = GetComponent<Player>();
    }

    void Update()
    {
        isUnlocked = !Input.GetKey(unlockMouse);

        //Input used to drive vehicle is in this section

        if (isUnlocked)
        {
            Cursor.lockState = CursorLockMode.Locked;
            Cursor.visible = false;
            player.turretAim.GetInput(Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime, Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime);
            player.turretAim.AimTurret();
        }
        //else statement exists, just changes cursor lock state and visibility
        
        if(Input.GetButton("Fire1")
        {
            player.cannon.TryFireShell();
            //player contains CannonBehavior
        }
    }
}
public class CannonBehavior : MonoBehaviour
{
    public Vector3 ShellFireOffset => GetShellOffset();

    public void TryFireShell()
    {
        //checks to see if cannon can fire, then gets a shell from the pool
        ShellBasic shellBehavior = shell.GetComponent<ShellBasic>();
        ShellPhysics shellPhys = shellBehavior.simplifiedPhysics;
        
        shell.SetActive(true);
        shellBehavior.Reset(ShellFireOffset, transform.forward.normalized);
    }

    public Vector3 GetShellOffset()
    {
        //going to use transforms here later
        return this.transform.position + this.transform.forward.normalized * this.fireOffset.z + this.transform.right.normalized * this.fireOffset.x + this.transform.up.normalized * this.fireOffset.y;
    }
}    
public class ShellBasic : MonoBehaviour
{
    public ShellPhysics simplifiedPhysics;

    void Awake()
    {
        this.simplifiedPhysics = this.GetComponent<ShellPhysics>();
    }

    public void Reset(Vector3 position, Vector3 direction)
    {
        this.simplifiedPhysics.FireFromPosition(position, direction, velocity);
    }
}
public class ShellPhysics : MonoBehaviour
{
    public Vector3 velocity;
    
    void FixedUpdate()
    {
        this.velocity += Physics.gravity * Time.fixedDeltaTime;
        this.transform.forward = this.velocity.normalized;
        this.transform.position += this.velocity * Time.fixedDeltaTime;
    }

    public void FireFromPosition(Vector3 position, Vector3 direction, float velocity)
    {
        this.transform.forward = direction;
        this.transform.position = position;
        this.velocity = this.transform.forward.normalized * velocity;
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ Two common ways for this to happen: 1) The shell gets positioned just before the barrel's aiming script runs, so it gets placed accurately, then the barrel moves out from under it. This can be particularly pronounced if one or the other is a physics object moving in the fixed time step. You don't see this effect with the gizmo because the gizmo logic runs at the end of the frame after everything else is in its final position. 2) The shell's collider overlaps the barrel's, and the physics engine gives it a kick to remove the overlap. Can you test to eliminate either possibility? \$\endgroup\$ Commented May 4, 2023 at 16:59
  • \$\begingroup\$ @DMGregory Both possibilities have already been eliminated, though I did not elaborate on this post. After modifying the script execution order to have the cannon firing occur after all physics and motion, the issue persists. To add, the shell has no collider to speak of, neither does the cannon. The shell also runs off of a custom physics script that just moves the shell based on velocity and adds gravity every physics frame, so it is not a rigidbody. Despite this, the issue existed back when I still had the shell as a rigidbody. \$\endgroup\$ Commented May 4, 2023 at 17:06
  • \$\begingroup\$ Can you edit your post to contain the code and setup steps needed to create a Minimal Complete Verifiable Example? Once we can reproduce the problem independently, we can test possible fixes to be sure they'll work without back-and-forth or redundantly suggesting things you've already tried. \$\endgroup\$ Commented May 4, 2023 at 18:02
  • \$\begingroup\$ @DMGregory Just updated post with what should be all necessary information. Code is not directly copied from my files, instead shortened to only display information relevant to shell firing and handling. I made sure of this. I also included the hierarchy and what each component is attached to. \$\endgroup\$ Commented May 4, 2023 at 19:37
  • \$\begingroup\$ To add on to my previous comment, this offset issue also appears to only occur when the gun is aiming up or down. It appears to be perfectly on target when the gun is level with the ground. \$\endgroup\$ Commented May 4, 2023 at 20:14

1 Answer 1

0
\$\begingroup\$

SOLVED The issue was caused by shells firing on Update, when vehicle physics happened on FixedUpdate. When the shell fired, it was not synced to the vehicle's physics. I fixed the issue by making shells fire on FixedUpdate instead, as technically, shell firing is a physics operation anyways.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I'll mention that timing differences between Update an FixedUpdate were one of the first probable causes I suggested, that you said you'd already tested and eliminated. 😜 Make sure you actually test first next time, to avoid terminating lines of inquiry that could yet prove fruitful. \$\endgroup\$ Commented May 5, 2023 at 1:45
  • \$\begingroup\$ @DMGregory technically, The inaccuracy was more down to shells firing asynchronously with physics updates than script execution order, which is what you first mentioned in your post. I "eliminated" that possibility because the turret was aimed in Update() as well, instead of in FixedUpdate. I simply neglected to account for my raycast wheel system which was the true root cause. While I appreciate the help narrowing it down, I do not appreciate snarky comments. \$\endgroup\$ Commented May 5, 2023 at 22:36

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.