Faux Rail-Shooter Help

Hey everyone,

I’m working on creating a “Faux Rail-Shooter,” where you control the ship on the screen by moving your mouse, and control the camera with WASD. Think of it like flight combat in Star Wars: The Old Republic or Star Fox.

The idea is that a ship follows a reticle on the X Y axis, and the reticle and ship are hard-attached to the camera. The camera constantly moves forward, and can yaw, pitch and roll.

Here’s a diagram:

The problem I’m having is that the ship isn’t rotating correctly with the camera - facing it’s starting rotation, and not along the camera’s forward. Also, the picking plane doesn’t seem to be right, but that may be due to the incorrect rotation of the ship. So, baby steps first.

Anyone willing to lend a hand?

for picking positions on a plane i suggest: http://unifycommunity.com/wiki/index.php?title=Click_To_Move

also it would help when you post your code to help improve as build your wrong code from your description to find the error may be hard.

also i would parent the camera to the ship and not vice versa as the ship is the object which should be moved and the camera positioned relative to it. but i must admit that i dont know your example games. shall the controlls be similar to freelancer?

Well, here’s a quick example of the effect I’m trying to get: http://www.youtube.com/watch?v=rjezvmnXzPo

The camera is “following” the player, in a way, however the player is free to move around inside the bounds of the screen’s frustum. My implementation would be slightly simpler, as I really don’t need the LookAt functionality of the camera.

I’ll be able to post code later, however I wanted to avoid doing that, because I don’t want to be given the answer, I want the concept of how to do it so I can really learn it, instead of copy-pasting code.

I’m a masochist like that, I guess.

i would set up the ship in a certain distance from the camera and move both with same speed towards one direction. in further distance i would use the clicktomove script i posted above to calculate the point the ship needs to fly toward. then let your ship rotate towards that point and move it there.

but the example indicates that you need a very small distance from targetplane to ship or have a small view frustum angle so that your ship cannot move outside of the camera view. you could even consider an orthografic camera for that.

if you want the ship to rotate i would introduce an threshold angle and when the angle of the players direction changes more than that over some frames do a roll.

what do you expect then? without code all you may get are these vague hints/ideas as probably noone would write such a system for you (what you dont want anyway seemingly). when you post your code and describe detailed what it does wrong one can help you. at least i’m not a masochist so if you intend to get help you should make it as convenient and time efficient as you can for any possible helper.

You have a valid point. Here’s the code:

FauxRailCamera - This is the script the camera uses to rotate around. None of the constant forward movement stuff has been implemented yet.

using UnityEngine;
using System.Collections;

public class FauxRailCamera : MonoBehaviour 
{
    public float PitchSpeed = 5.0f;
    public float YawSpeed = 5.0f;
    public float RollSpeed = 5.0f;

    private Transform childFollow = null;

    void OnGUI()
    {
    }

    void Awake()
    {
        childFollow = transform.Find("Ship");
    }
	// Use this for initialization
	void Start () 
    {
	
	}
	
	// Update is called once per frame
	void Update () 
    {
        Quaternion AddRot = Quaternion.identity;
        float roll = 0;
        float pitch = 0;
        float yaw = 0;

        roll = Input.GetAxis("Roll") * (Time.deltaTime * RollSpeed);
        pitch = Input.GetAxis("Pitch") * (Time.deltaTime * PitchSpeed);
        yaw = Input.GetAxis("Yaw") * (Time.deltaTime * YawSpeed);

        AddRot.eulerAngles = new Vector3(-pitch, yaw, -roll);

        transform.rotation *= AddRot;
	}
}

Reticle - Tracks the mouse pointer and converts it to a world coordinate.

using UnityEngine;
using System.Collections;

public class Reticle : MonoBehaviour 
{
    public float ZPlane = 15.0f;

    private Camera mainCamera;

    void OnGui()
    {
    }

	// Use this for initialization
	void Start () 
    {
        mainCamera = Camera.main;
	}
	
	void Update () 
    {
        Vector3 mousepos = Input.mousePosition;
        mousepos.z = ZPlane;
        transform.position = mainCamera.ScreenToWorldPoint(mousepos);

	}
}

LocalFollowController - Follows the Reticle around in local space relative to the parent (or at least it should).

using UnityEngine;
using System.Collections;

public class LocalFollowController : MonoBehaviour {

    private Reticle reticle;

    public float MoveSpeed = 10.0f;
    public float RotateSpeed = 10.0f;

    void OnGUI()
    {
    }

	// Use this for initialization
	void Start () 
    {
        reticle = GameObject.FindGameObjectWithTag("Reticle").GetComponent<Reticle>();
	}
	
	// Update is called once per frame
	void Update () 
    {
        Vector3 mousePos = reticle.transform.position;

        Vector3 aimdir = mousePos - transform.position;
        Quaternion Follow = Quaternion.LookRotation(aimdir);
        transform.rotation = Quaternion.Slerp(transform.localRotation, Follow, Time.deltaTime * RotateSpeed);

        Vector3 position = transform.localPosition;

        position.x = Mathf.Lerp(position.x, mousePos.x, Time.deltaTime * MoveSpeed);
        position.y = Mathf.Lerp(position.y, mousePos.y, Time.deltaTime * MoveSpeed);

        transform.localPosition = position;
	}
}

so the ship should always be in the same position relative to the camera? that you see it from the same angle? then you should set the camera at a certain position behind the ship and let it look at it. but this not what your example shows.

        roll = Input.GetAxis("Roll") * (Time.deltaTime * RollSpeed);
        pitch = Input.GetAxis("Pitch") * (Time.deltaTime * PitchSpeed);
        yaw = Input.GetAxis("Yaw") * (Time.deltaTime * YawSpeed);

so you controll the camera with 3 axis and additional you want to controll the ship with the mouse? are you sure that goes along? i would go another aproach that the ship is controlled by mouseclicks and the camera is positioned properly behinde the ship.
i’m not sure if these 2 approaches work together as you force the camera in a certain rotation and the only way to rotate the ship the same way is to set it to the rotation of the camera.
also it will be hard to keep the ship in camera view when it heads towards the direction you clicked last and the camera moves in another direction.
so this all seems a bit quirky to me. however maybe i dont completely understand how the behavior should be as your description is significantly different from your example.

you should provide more detail of how it should be.

For a camera system like this, what I would personally do is set the camera back an offset amount from the Players ship but bound it within a Rectangle. Multiply the ships Transform.Vector3.fwd component by a constant to project where the reticle should be pointing at , use this end point as a focal point for your Camera to LookAt. Then you set another bounds for the Angles the camera can point at, say ±15 degrees. That way your camera will always be pointing at whatever the ship is pointing towards within those bounds. I think that would mirror the effect in star fox, but I’m not certain.

exiguous, I do believe we are getting ideas mixed up a bit.

Here is an image from Star Fox 64:

Star Fox is an “On-Rail” third-person shooter. Much like you’d see in arcades, the game progresses you through the level, and you control the crosshairs and fire. As you move the crosshairs in Star Fox 64, your ship follows along. You only really move along the local X and Y axis on the screen, while the camera moves along the Z axis. Occasionally, the camera would rotate, turn and perform other actions regarding direction.

So think of this as an arcade shooter. You basically point reticle, and the camera handles your direction. The only difference here is now there’s a space ship between the camera and the reticle.

Bam! Figured it out!

I implemented the picking code exiguous posted up in my Reticle class, and found that my problem was just a big case of over-complication. I had all the data I needed to get the effected I wanted, but I was needlessly manipulating it.

Here’s the revised code to the 3 classes. Thanks guys! One day I’ll make a post about this on my blog :slight_smile:

FauxRailCamera

using UnityEngine;
using System.Collections;

public class FauxRailCamera : MonoBehaviour 
{
    public float PitchSpeed = 5.0f;
    public float YawSpeed = 5.0f;
    public float RollSpeed = 5.0f;

    private Transform childFollow = null;

    void OnGUI()
    {
    }

    void Awake()
    {
        childFollow = transform.Find("Ship");
    }
	// Use this for initialization
	void Start () 
    {
	
	}
	
	// Update is called once per frame
	void Update () 
    {
        Quaternion AddRot = Quaternion.identity;
        float roll = 0;
        float pitch = 0;
        float yaw = 0;

        roll = Input.GetAxis("Roll") * (Time.deltaTime * RollSpeed);
        pitch = Input.GetAxis("Pitch") * (Time.deltaTime * PitchSpeed);
        yaw = Input.GetAxis("Yaw") * (Time.deltaTime * YawSpeed);

        AddRot.eulerAngles = new Vector3(-pitch, yaw, -roll);
        transform.rotation *= AddRot;
	}
}

LocalFollowController

using UnityEngine;
using System.Collections;

public class LocalFollowController : MonoBehaviour {

    private Reticle reticle;

    public float MoveSpeed = 10.0f;
    public float RotateSpeed = 10.0f;

    void OnGUI()
    {
    }

	// Use this for initialization
	void Start () 
    {
        reticle = GameObject.FindGameObjectWithTag("Reticle").GetComponent<Reticle>();
	}
	
	// Update is called once per frame
	void Update () 
    {
        Vector3 mousePos = reticle.TargetPosition;//reticle.transform.position;

        Vector3 aimdir = mousePos - transform.position;
        Quaternion Follow = Quaternion.LookRotation(reticle.TargetDirection, reticle.transform.up);
        transform.rotation = Follow;

        transform.position = Vector3.Lerp(transform.position, mousePos, Time.deltaTime * MoveSpeed);
	}
}

Reticle

using UnityEngine;
using System.Collections;

public class Reticle : MonoBehaviour 
{
    public Vector3 TargetPosition = Vector3.zero;
    public Vector3 TargetDirection = Vector3.forward;
    public float ZPlane = 15.0f;

    private Camera mainCamera;

    void OnDrawGizmos()
    {
        Gizmos.DrawSphere(TargetPosition, 0.5f);
    }
    void OnGui()
    {
    }

	// Use this for initialization
	void Start () 
    {
        mainCamera = Camera.main;
	}
	
	// Update is called once per frame
	void Update () 
    {
        transform.rotation = Quaternion.LookRotation(-mainCamera.transform.forward, mainCamera.transform.up);
        Vector3 mousepos = Input.mousePosition;
        Plane pickplane = new Plane(transform.forward, transform.position);
        Debug.DrawLine(transform.position, transform.forward * 2);
        
        Ray pickray = mainCamera.ScreenPointToRay(mousepos);
        float hitdist = 0.0f;

        if(pickplane.Raycast(pickray, out hitdist))
        {
            TargetPosition = pickray.GetPoint(hitdist);
            TargetDirection = pickray.direction;
        }
        else
        {
            TargetPosition = TargetPosition;
        }

	}
}

Thank you Sass

This probably counts as bumping, so I apologize in advance - however I can’t figure this out.

The FauxRailCamera goes on the main camera, the Reticle goes on the reticle and the LocalFollowController on the ship? That would make most sense to me but alas it doesn’t work. My ship -is- following the Reticle but rotated 90 degrees on it’s Z-Axis and it’s not moving forward.

Could someone point me in the right direction as to what the hierarchy is and/or the objects I should add the scripts to?