Jim's
Tutorials

Spring 2018
course
site

Copernicus (version title)

Object Structure

Since I showed the basic gameplay in the last tutorial, I'll just start with the inner workings. Unity works with a list of GameObjects that exist in the world. Here's the basic structure before pressing play:

After pressing play and adding some stuff

It looks more like

A new Ship is added when you place a single block down (holding alt). It uses ShipController.cs. All of the parts of that ship become children of the Ship object.

When the Player attaches to a Ship, it becomes a child of that Ship.

All the "(Clone)" stuff is just saying that the object was made from a prefab, which as far as I can tell is just like and instance made from a class.

Code Structure

Every moving piece in the game will have it's own directory, each with a ThingnameController.cs in it. If it's an item it will also have a ThingnameItemController.cs. For items that there are likely to be many types of (e.g. flooring), there is a BaseThingnameController.cs and a BaseThingnameItemController.cs. Everything inherits from the master files BaseController.cs and BaseItemController.cs. Here's an example of the IronFlooring item:

I've tried to keep as much code as possible local to the objects themselves. For instance, the code for adding flooring lies in BaseFlooringItemController.sc as opposed to the ShipController.cs file. With all the logic and assets contained in one spot, adding items into the game should be as simple as making a new directory and linking it somewhere.

Code Itself

The important scripts so far are PlayerController, ShipController, the item controllers. I commented most of the discrete sections of code.

PlayerController.cs

Probably the most complex thing here is the part handling player movement when attached to a ship. Unity physics don't work in local space, so I just had to handle collisions with Vectors and Math.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour 
{
    // Public variables { get; set; }
    public int thruster_power;
    public float walking_speed_cap;
    public float walking_acceleration;
    public float rotation_speed;
    public int current_direction;
    public Transform inventory_object;
    public Dictionary<string, object> input;

    // Public variables { get; }
    public Rigidbody2D body
    {
        get
        {
            return _body;
        }
    }
    public CanvasController canvas_controller
    {
        get
        {
            return _canvas_controller;
        }
    }

    // Private constants
    private Animator animator;
    private ParticleSystem thruster_up;
    private ParticleSystem thruster_down;
    private ParticleSystem thruster_left;
    private ParticleSystem thruster_right;
    private Rigidbody2D _body;
    private CanvasController _canvas_controller;
    private new Camera camera;

    // Private variables
    private GameObject parent_ship;
    private GameObject[] inventory;
    private int index_selected;
    private SpriteRenderer destroy_renderer;
    private Vector2 movement_force;

    void Start ()
    {

        // Main setup
        animator = GetComponent<Animator>();
        _body = GetComponent<Rigidbody2D>();
        _canvas_controller = GetComponentInChildren<CanvasController>();
        camera = gameObject.GetComponentInChildren<Camera>();
        input = new Dictionary<string, object>();
        fetchInput();


        // Inventory setup
        inventory = new GameObject[50];
        inventory[0] = Instantiate(Resources.Load("Items/Tool/DestroyerTool/DestroyerToolItem"), inventory_object) as GameObject;
        inventory[1] = Instantiate(Resources.Load("Items/Flooring/IronFlooring/IronFlooringItem"), inventory_object) as GameObject;
        inventory[2] = Instantiate(Resources.Load("Items/Wall/IronWall/IronWallItem"), inventory_object) as GameObject;
        inventory[3] = Instantiate(Resources.Load("Items/Machine/MagnetizerMachine/MagnetizerMachineItem"), inventory_object) as GameObject;
        inventory[4] = Instantiate(Resources.Load("Items/Hull/IronHull/IronHullItem"), inventory_object) as GameObject;
        index_selected = 0;

        // Particle thruster setup
        ParticleSystem[] thrusters = transform.GetComponentsInChildren<ParticleSystem>();
        foreach (ParticleSystem thruster in thrusters)
        {
            switch (thruster.name)
            {
            case "Thruster Up":
                thruster_up = thruster;
                break;
            case "Thruster Down":
                thruster_down = thruster;
                break;
            case "Thruster Left":
                thruster_left = thruster;
                break;
            case "Thruster Right":
                thruster_right = thruster;
                break;
            }
        }
    }

    public Vector2 getPlacementVelocity(Vector2 xy)
    {
        if (parent_ship)
        {
            return parent_ship.GetComponent<Rigidbody2D>().GetPointVelocity(xy);
        }
        else
        {
            return body.velocity;
        }
    }

    private bool connectToShip()
    {
        RaycastHit2D[] nearby = Physics2D.LinecastAll(transform.position, transform.position);
        foreach (RaycastHit2D rayhit in nearby)
        {
            GameObject ship = rayhit.transform.gameObject;
            ShipController ship_controller = ship.GetComponent<ShipController>();
            if (ship_controller)
            {
                if (ship_controller.magnetized(transform.position))
                {
                    transform.SetParent(ship.transform);
                    body.velocity = Vector2.zero;
                    parent_ship = ship;
                    body.isKinematic = true;
                    movement_force = Vector2.zero;

                    PlatformEffector2D platform_effecter = gameObject.AddComponent(typeof(PlatformEffector2D)) as PlatformEffector2D;
                    platform_effecter.useColliderMask = false;
                    platform_effecter.surfaceArc = 0;
                    gameObject.GetComponent<Collider2D>().usedByEffector = true;

                    return true;
                }
            }
        }
        return false;
    }

    private void disconnectFromShip()
    {
        Vector2 new_position = transform.position;
        Vector2 new_velocity = parent_ship.GetComponent<Rigidbody2D>().GetPointVelocity(transform.position);

        transform.SetParent(null);
        parent_ship = null;
        body.isKinematic = false;
        Destroy(gameObject.GetComponent<PlatformEffector2D>());
        gameObject.GetComponent<Collider2D>().usedByEffector = false;

        transform.position = new_position;
        body.velocity = new_velocity;
    }

    private Vector2 applyCollisions(Vector2 force)
    {
        ContactPoint2D[] contacts = new ContactPoint2D[20];
        int contact_count = gameObject.GetComponent<Collider2D>().GetContacts(contacts);

        Vector2 collision_force = Vector2.zero;
        Vector2[] collision_normals = new Vector2[contact_count];
        for (int i = 0; i < contact_count; i++)
        {
            if (contacts[i].enabled)
            {
                throw new System.ArgumentException("Collision enabled in ship mode.");
            }
//          Debug.DrawRay(transform.position, contacts[i].normal, Color.magenta, 0.3f, false);
            collision_normals[i] = contacts[i].normal;
            collision_force += contacts[i].normal * -contacts[i].separation * _globals.PLAYERCOLLISIONSTRENGTH;
        }

        if (contact_count > 0)
        {
            // Find most opposing collision
            float min_dot = float.MaxValue;
            Vector2 steepest_collision = Vector2.negativeInfinity;
            int steepest_collision_index = -1;
            for (int i = 0; i < contact_count; i++)
            {
                Vector2 collision_normal = collision_normals[i];
                float dot = Vector2.Dot(force.normalized, collision_normal);
                if (dot < min_dot)
                {
                    min_dot = dot;
                    steepest_collision = collision_normal;
                    steepest_collision_index = i;
                }
            }

            // Check if most opposiong collision is opposing
            if (min_dot < 0)
            {
                // Redirect force
                Vector2 tangent = new Vector2(-steepest_collision.y, steepest_collision.x);
                force = Vector3.Project(force, tangent);

                // Check if there are other opposiong collisions
                for (int i = 0; i < contact_count; i++)
                {
                    if (i != steepest_collision_index)
                    {
                        Vector2 collision_normal = collision_normals[i];
                        if (Vector2.Dot(force.normalized, collision_normal) < 0)
                        {
                            force = Vector2.zero;
                            break;
                        }
                    }
                }
            }
        }

        force += collision_force;

        //      Debug.DrawRay(transform.position, force.normalized, Color.cyan, 0.3f, false);

        return force;
    }

    private void fetchInput()
    {
        input["mouse_st"]         = (Vector2)Input.mousePosition;
        input["mouse_xy"]         = (Vector2)camera.ScreenToWorldPoint((Vector2)input["mouse_st"]);
        input["mouse_clicked"]    = Input.GetMouseButtonDown(_globals.LEFTMOUSEINDEX);
        input["mouse_down"]       = Input.GetMouseButton(_globals.LEFTMOUSEINDEX);
        input["mouse_released"]   = Input.GetMouseButtonUp(_globals.LEFTMOUSEINDEX);
        input["rmouse_clicked"]   = Input.GetMouseButtonDown(_globals.RIGHTMOUSEINDEX);
        input["rmouse_down"]      = Input.GetMouseButton(_globals.RIGHTMOUSEINDEX);
        input["rmouse_released"]  = Input.GetMouseButtonUp(_globals.RIGHTMOUSEINDEX);
        input["axis_horizontal"]  = Input.GetAxis("Horizontal");
        input["axis_vertical"]    = Input.GetAxis("Vertical");
        input["esc"]              = Input.GetKeyDown(KeyCode.Escape);
        input["modify"]           = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
        input["rotate_cw"]        = Input.GetKey(KeyCode.E);
        input["rotate_ccw"]       = Input.GetKey(KeyCode.Q);
        input["magnetize"]        = Input.GetKeyDown(KeyCode.C);
        input["set_destroy_mode"] = Input.GetKeyDown(KeyCode.Backspace);
        input["inv_1"]            = Input.GetKeyDown(KeyCode.Alpha1);
        input["inv_2"]            = Input.GetKeyDown(KeyCode.Alpha2);
        input["inv_3"]            = Input.GetKeyDown(KeyCode.Alpha3);
        input["inv_4"]            = Input.GetKeyDown(KeyCode.Alpha4);
        input["inv_5"]            = Input.GetKeyDown(KeyCode.Alpha5);
        input["inv_6"]            = Input.GetKeyDown(KeyCode.Alpha6);
        input["inv_7"]            = Input.GetKeyDown(KeyCode.Alpha7);
        input["inv_8"]            = Input.GetKeyDown(KeyCode.Alpha8);
        input["inv_9"]            = Input.GetKeyDown(KeyCode.Alpha9);
    }

    private void selectInventorySlot(int slot_index)
    {
        if (index_selected != slot_index)
        {
            if (index_selected >= 0 && inventory[index_selected])
            {
                inventory[index_selected].GetComponent<BaseItemController>().deselect();
            }
            index_selected = slot_index;
            inventory[index_selected].GetComponent<BaseItemController>().select();
        }
    }

    private void deselectInventorySlot()
    {
        if (index_selected >= 0 && inventory[index_selected])
        {
            inventory[index_selected].GetComponent<BaseItemController>().deselect();
            index_selected = -1;
        }
    }

    void Update ()
    {
        fetchInput();

        // Rotate Player
        if ((bool)input["rotate_cw"])
        {
            transform.Rotate(0, 0, -rotation_speed);
        }
        if ((bool)input["rotate_ccw"])
        {
            transform.Rotate(0, 0, rotation_speed);
        }

        // Select Inventory Slot
        if ((bool)input["inv_1"])
        {
            selectInventorySlot(1);
        }
        if ((bool)input["inv_2"])
        {
            selectInventorySlot(2);
        }
        if ((bool)input["inv_3"])
        {
            selectInventorySlot(3);
        }
        if ((bool)input["inv_4"])
        {
            selectInventorySlot(4);
        }
        if ((bool)input["inv_5"])
        {
            selectInventorySlot(5);
        }
        if ((bool)input["inv_6"])
        {
            selectInventorySlot(6);
        }
        if ((bool)input["inv_7"])
        {
            selectInventorySlot(7);
        }
        if ((bool)input["inv_8"])
        {
            selectInventorySlot(8);
        }
        if ((bool)input["inv_9"])
        {
            selectInventorySlot(9);
        }

        // Magnetizer
        if ((bool)input["magnetize"])
        {
            if (parent_ship)
            {
                disconnectFromShip();
            }
            else
            {
                connectToShip();
            }
        }

        // Change Mouse Modes
        if ((bool)input["set_destroy_mode"])
        {
            selectInventorySlot(0);
        }
        if ((bool)input["esc"])
        {
            if (index_selected >= 0 && inventory[index_selected].GetComponent<BaseItemController>().mid_place)
            {
                inventory[index_selected].GetComponent<BaseItemController>().deselect();
                inventory[index_selected].GetComponent<BaseItemController>().select();
            }
            else
            {
                deselectInventorySlot();
            }
        }

        GameObject item_held = null;
        if (index_selected >= 0)
        {
            item_held = inventory[index_selected];
        }

        // Do Mouse Action
        if (item_held)
        {
            if ((bool)input["mouse_clicked"])
            {
                item_held.GetComponent<BaseItemController>().click();
            }
            else if ((bool)input["mouse_released"])
            {
                item_held.GetComponent<BaseItemController>().release();
            }
            else if ((bool)input["mouse_down"])
            {
                item_held.GetComponent<BaseItemController>().drag();
            }
            else if ((bool)input["rmouse_clicked"])
            {
                item_held.GetComponent<BaseItemController>().rclick();
            }
            else if ((bool)input["rmouse_released"])
            {
                item_held.GetComponent<BaseItemController>().rrelease();
            }
            else if ((bool)input["rmouse_down"])
            {
                item_held.GetComponent<BaseItemController>().rdrag();
            }
            else
            {
                item_held.GetComponent<BaseItemController>().hover();
            }
        }
    }

    void FixedUpdate()
    {
        // Movement handled in FixedUpdate() so it plays nice with physics

        float moveHorizontal = (float)input["axis_horizontal"];
        float moveVertical   = (float)input["axis_vertical"];
        animator.SetFloat("dx", moveHorizontal);
        animator.SetFloat("dy", moveVertical);

        // Check if ship floor is under Player's feet, if not disconnect
        if (!parent_ship.GetComponent<ShipController>().magnetized(transform.position))
        {
            disconnectFromShip();
        }

        if (!parent_ship) // If Player is in Space Walk mode, i.e. player is not on a ship
        {
            // Do thruster animation
            if (moveVertical > 0)
            {     // Up
                thruster_down.Emit(thruster_power * 2);
            }
            if (moveVertical < 0)
            {     // Down
                thruster_up.Emit(thruster_power * 2);
            }
            if (moveHorizontal > 0)
            {     // Right
                thruster_left.Emit(thruster_power * 2);
            }
            if (moveHorizontal < 0)
            {     // Left
                thruster_right.Emit(thruster_power * 2);
            }

            // Add force to Player (let Unity physics handle collisions etc.)
            Vector2 force = body.GetRelativeVector(new Vector2(moveHorizontal, moveVertical) * thruster_power * body.mass);
            body.AddForceAtPosition(force, transform.position, ForceMode2D.Force);
        }

        else // If Player is in 'magnetized' mode, i.e. player is standing on a ship
        {
            // Add force to Player (with math)

            Vector2 input_force = new Vector2(moveHorizontal, moveVertical);


            // Call collision handling function
            Vector2 collided_input_force = applyCollisions(Quaternion.AngleAxis(transform.rotation.eulerAngles.z, Vector3.forward) * input_force.normalized);


            // TODO Comment this section

            Vector2 collided_input_normal = Quaternion.AngleAxis(-transform.rotation.eulerAngles.z, Vector3.forward) * collided_input_force.normalized;


            if (Vector2.Dot(movement_force, input_force) <= 0)
            {
                movement_force = Vector2.zero;
            }

            ////////////////////////////////////////////
            Vector2 collided_input_tangent = new Vector2(-collided_input_normal.y, collided_input_normal.x);
            Vector2 parallel = Vector3.Project(movement_force, collided_input_normal.normalized);
            Vector2 perpendicular = Vector3.Project(movement_force, collided_input_tangent.normalized);

            if (perpendicular.magnitude < walking_acceleration)
            {
                perpendicular = Vector2.zero;
            }
            else
            {
                perpendicular -= perpendicular.normalized * walking_acceleration;
            }

            movement_force = perpendicular + parallel;
            ////////////////////////////////////////////

//              Debug.DrawRay(transform.position, Quaternion.AngleAxis(transform.rotation.eulerAngles.z, Vector3.forward) * collided_input_normal, Color.red, 0.3f, false);

            Vector2 new_movement_force = movement_force + input_force.normalized * walking_acceleration;

            float speed_cap = walking_speed_cap;
            if (Input.GetKey(KeyCode.LeftShift))
            {
                speed_cap *= 2;
            }
            if (new_movement_force.magnitude < speed_cap)
            {
                movement_force = new_movement_force;
            }
            else
            {
                movement_force = new_movement_force.normalized * walking_speed_cap;
            }

            Vector2 rotated_force = Quaternion.AngleAxis(transform.rotation.eulerAngles.z, Vector3.forward) * movement_force;
            Vector2 collided_force = applyCollisions(rotated_force);
            movement_force = Quaternion.AngleAxis(-transform.rotation.eulerAngles.z, Vector3.forward) * collided_force;

            transform.position = new Vector2(transform.position.x + collided_force.x, transform.position.y + collided_force.y);
        }
    }
}

ShipController.cs

Not much happens here yet. There's a few dictionaries for holding flooring blocks and walls. This is what most of the file is about:

//===============================================================//
//=======> Conversions <=========================================//
// ab -- unknown
// st -- screen
// xy -- world
// uv -- local
// ef -- diagonal
// mn -- floor
// op -- corners
// gh -- wall

This is the system I've been using for coordinates. There are so many types of coordinates, and I've found having a convension useful. Most of the functions under the conversions section take a Vector2 and return a Vector2, though some return an array of Vector2s. This is because each floor has 4 corners, each floor has 4 walls, each wall has 2 floors, etc.

On the right here is an example of the mn and gh grids, and the math for converting between them. The gh grid is horizontal because of the nature of walls.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShipController : MonoBehaviour
{

    public Dictionary<Vector2, GameObject> floor_blocks = new Dictionary<Vector2, GameObject>();
    public Dictionary<Vector2, GameObject[]> hull_points = new Dictionary<Vector2, GameObject[]>();
    public Dictionary<Vector2, GameObject> walls = new Dictionary<Vector2, GameObject>();
    public Dictionary<Vector2, GameObject> machines = new Dictionary<Vector2, GameObject>();
    public int magnetization;

    private Rigidbody2D body;

    void OnEnable()
    {
        body = GetComponent<Rigidbody2D>();
        magnetization = 1;
    }

    void Update() {}

    public float rotation
    {
        get
        {
            return body.rotation;
        }
    }



    public bool magnetized(Vector2 xy)
    {
        return floor_blocks.ContainsKey(xy_to_mn(xy)) && magnetization > 0;
    }

    public float compositeRotation(float item_rotation)
    {
        return item_rotation + transform.rotation.eulerAngles.z;
    }

    public Vector2 newWallPosition()
    {
        return Vector2.negativeInfinity;
    }

    public void AddFloor(GameObject flooring, Vector2 mn)
    {
        if (floor_blocks.ContainsKey(mn))
        {
            throw new System.ArgumentException("Adding floor block: cannot add onto existing block");
        }
        floor_blocks.Add(mn, flooring);
    }

    int Round(float num)
    {
        // Trust me this works
        if (num > -0.5f)
        {
            return (int)(num + 0.5f);
        }
        else
        {
            return (int)(num - 0.5f);
        }
    }

    Vector2 RoundVector2(Vector2 vector)
    {
        return new Vector2(Round(vector[0]), Round(vector[1]));
    }





    //===============================================================//
    //=======> Conversions <=========================================//
    // ab -- unknown
    // st -- screen
    // xy -- world
    // uv -- local
    // ef -- diagonal
    // mn -- floor
    // op -- corners
    // gh -- wall

    public Vector2 xy_to_mn(Vector2 xy)
    {
        return uv_to_mn(xy_to_uv(xy));
    }

    public Vector2 xy_to_uv(Vector2 xy)
    {
        return body.GetPoint(xy);
    }

    public Vector2 uv_to_mn(Vector2 uv)
    {
        return scale_down_ab(uv, 1);
    }

    public Vector2 scale_down_ab(Vector2 ab, float scale)
    {
        return RoundVector2(ab / (_globals.floor_block_width / scale));
    }

    public Vector2 scale_up_ab(Vector2 ab, float scale)
    {
        return ab * (_globals.floor_block_width / scale);
    }

    public Vector2 uv_to_gh(Vector2 uv)
    {
        return scale_down_ab(ab_to_ef(uv + (Vector2.up * _globals.floor_block_width / 2)), 1);
    }

    public Vector2 mn_to_gh(Vector2 mn, Vector2 side)
    {
        Vector2 down = ab_to_ef(mn);
        if (side == Vector2.up)
        {
            return down + Vector2.up + Vector2.right;
        }
        else if (side == Vector2.left)
        {
            return down + Vector2.up;
        }
        else if (side == Vector2.right)
        {
            return down + Vector2.right;
        }
        else if (side == Vector2.down)
        {
            return down;
        }
        return Vector2.negativeInfinity;
    }

    public Vector2 ab_to_ef(Vector2 ab)
    {
        return new Vector2(ab.y + ab.x, ab.y - ab.x);
    }

    public Vector2 ef_to_ab(Vector2 ef)
    {
        float a = (ef.x - ef.y) / 2;
        float b = ef.x - a;
        return new Vector2(a, b);
    }

    public Vector2[] gh_to_mn(Vector2 gh)
    {
        Vector2 mn = ef_to_ab(gh);
        float m = mn.x;
        float n = mn.y;

        Vector2[] result = new Vector2[2];
        if ((gh.x - gh.y) % 2 == 0)
        {
            result[0] = RoundVector2(new Vector2(m, n));
            result[1] = RoundVector2(new Vector2(m, n-1));
        }
        else
        {
            result[0] = RoundVector2(new Vector2(m-0.5f, n-0.5f));
            result[1] = RoundVector2(new Vector2(m+0.5f, n-0.5f));
        }
        return result;
    }

    public Vector2 gh_to_uv(Vector2 gh)
    {
        return ef_to_ab(scale_up_ab(gh, 1)) - (Vector2.up * _globals.floor_block_width / 2);
    }

    public Vector2 mn_to_uv(Vector2 mn)
    {
        return scale_up_ab(mn, 1);
    }

    public Vector2 uv_to_xy(Vector2 uv)
    {
        return body.GetRelativePoint(uv);
    }

    public Vector2 mn_to_xy(Vector2 mn)
    {
        return uv_to_xy(mn_to_uv(mn));
    }

    public Vector2 uv_to_op(Vector2 uv)
    {
        return uv_to_mn(uv - new Vector2(_globals.floor_block_width / 2, _globals.floor_block_width / 2));
    }

    public Vector2 op_to_uv(Vector2 op)
    {
        return mn_to_uv(op) + new Vector2(_globals.floor_block_width / 2, _globals.floor_block_width / 2);
    }

    public Vector2 op_to_xy(Vector2 op)
    {
        return uv_to_xy(op_to_uv(op));
    }

    public Vector2 xy_to_op(Vector2 xy)
    {
        return uv_to_op(xy_to_uv(xy));
    }

    public Vector2[] mn_to_op(Vector2 mn)
    {
        Vector2[] op = new Vector2[4];
        op[0] = mn;
        op[1] = mn - Vector2.up;
        op[2] = mn - (Vector2.up + Vector2.right);
        op[3] = mn - Vector2.right;
        return op;
    }

    public Vector2[] op_to_mn(Vector2 op)
    {
        Vector2[] mn = new Vector2[4];
        mn[0] = op;
        mn[1] = op + Vector2.up;
        mn[2] = op + Vector2.up + Vector2.right;
        mn[3] = op + Vector2.right;
        return mn;
    }

    //==============================================//
}

BaseItemController.cs

This is the base class that all items inherit from. When Player has an item selected it calls each of these functions based on mouse input.

public virtual void hover() {}
public virtual void click() {}
public virtual void drag() {}
public virtual void release() {}

public virtual void select() {}
public virtual void deselect() {}

public virtual void rclick() {}
public virtual void rdrag() {}
public virtual void rrelease() {}

Also included is get_closest_valid_ship(). Since the user doesn't have to specify which ship they're placing items on, the target ship has to be determined so that's what this function's for. It calls the item specific get_placement_position() then finds the closest valid placement position. So when there are two or more places for an item can go it will pick the closest (img).

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BaseItemController : MonoBehaviour
{

    public float quantity;
    public bool mid_place;
    public GameObject place_object;

    protected PlayerController player_controller;
    protected Sprite place_sprite;
    protected Vector2 place_sprite_dimentions;
    protected float item_rotation;

    public virtual void hover() {}
    public virtual void click() {}
    public virtual void drag() {}
    public virtual void release() {}

    public virtual void select() {}
    public virtual void deselect() {}

    public virtual void rclick() {}
    public virtual void rdrag() {}
    public virtual void rrelease() {}

    protected virtual void StartBaseItem() {}

    void Start()
    {
        player_controller = GetComponentInParent<PlayerController>();
        if (place_object && place_object)
        {
            SpriteRenderer place_sprite_renderer = place_object.GetComponent<SpriteRenderer>();
            if (place_sprite_renderer)
            {
                place_sprite = place_sprite_renderer.sprite;
                place_sprite_dimentions = place_object.GetComponent<SpriteRenderer>().bounds.size;
            }
        }
        item_rotation = 0;
        StartBaseItem();
    }

    protected float place_rotation
    {
        get
        {
            return item_rotation + player_controller.body.rotation;
        }
    }

    protected GameObject get_closest_valid_ship(Vector2 xy)
    {
        GameObject closest_ship = null;
        float closest_sqr_distance = float.MaxValue;

        RaycastHit2D[] nearby = Physics2D.CircleCastAll(xy, _globals.scan_size, Vector2.up, 0);
        foreach(RaycastHit2D rayhit in nearby)
        {
            if(rayhit.transform.GetComponent<ShipController>())
            {
                GameObject target_ship = rayhit.transform.gameObject;
                Vector2 target_position = get_placement_position(xy, target_ship);
                if (target_position.x != Mathf.NegativeInfinity)
                {
                    float target_sqr_distance = Mathf.Pow(target_position.x - xy.x, 2) + Mathf.Pow(target_position.y - xy.y, 2);
                    if (target_sqr_distance < closest_sqr_distance)
                    {
                        closest_sqr_distance = target_sqr_distance;
                        closest_ship = target_ship;
                    }
                }
            }
        }

        return closest_ship;
    }

    protected virtual Vector2 get_placement_position(Vector2 xy, GameObject target_ship)
    {
        return Vector2.negativeInfinity;
    }

    protected void drawImage(Sprite sprite, Vector2 xy, Quaternion q_rotation, float width, float height, Color color)
    {
        player_controller.canvas_controller.drawImage(sprite, xy, q_rotation, width, height, color);
    }
}
https://cs.marlboro.college /cours /spring2018 /jims_tutorials /opengl /feb13mer
last modified Sun December 22 2024 5:31 am