Scriptable Objects?

Discuss modding questions and implementation details.
Post Reply
User avatar
Macadaynu
Posts: 261
Joined: Sun Mar 07, 2021 1:18 pm

Scriptable Objects?

Post by Macadaynu »

Do Unity's scriptable objects not work with the modding system?

I'm referring to these: https://docs.unity3d.com/Manual/class-S ... bject.html

They work fine when using the Unity editor with/without virtual mode, but when building/exporting a mod and actually trying it in game they don't seem to work. Reading the DFU docs it says:

A mod bundle can contain any kind of asset that derives from UnityEngine.Object, including textures, meshes, sounds, shaders etc.

Scriptable objects do derive from UnityEngine.Object so not sure why this won't work. Have also tried using the pre-compiled option when building the mod which didn't work.

Also is there a way I can see errors thrown when running the game outside of the editor? Like an output log where debug messages get stored?

User avatar
Hazelnut
Posts: 3015
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: Scriptable Objects?

Post by Hazelnut »

Macadaynu wrote: Mon May 24, 2021 8:46 pm Also is there a way I can see errors thrown when running the game outside of the editor? Like an output log where debug messages get stored?
Yes, look in your DFU user folder where settings and save go. There's a log file called Player.log.

No idea about the other stuff, sorry.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

User avatar
Macadaynu
Posts: 261
Joined: Sun Mar 07, 2021 1:18 pm

Re: Scriptable Objects?

Post by Macadaynu »

Hazelnut wrote: Mon May 24, 2021 9:58 pm
Macadaynu wrote: Mon May 24, 2021 8:46 pm Also is there a way I can see errors thrown when running the game outside of the editor? Like an output log where debug messages get stored?
Yes, look in your DFU user folder where settings and save go. There's a log file called Player.log.

No idea about the other stuff, sorry.
Thanks, I'll see if the logs can give me some clues

l3lessed
Posts: 1399
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: Scriptable Objects?

Post by l3lessed »

I do not think you need the editor to do any of that. You can create a constructor class to do the exact same thing, without the layers of the editor screwing with the compiler, then just attach the constructor class to whatever game object you want.

You have to build the object, and then create an empty game object during or after the start routine of the mod, and then attach the scriptable object to the empty game object using an AddComponent<type>. Note the "type" is actually the scriptable object in the unity example you linked.

Here is an example of how I attach the scriptable object to every npc in the streaming world location block called npcMarker. This adds all the needed objects and properties for the npc to show up properly on my minimap mod.

This is the constructor class, which is used to attach to an object, thus creating a scriptable object at run time.

Code: Select all

using System.Collections;
using UnityEngine;
namespace DaggerfallWorkshop.Game.Minimap
{
    public class npcMarker : MonoBehaviour
    {
        //object constructor class and properties for setting up, storing, and manipulating specific object properties.
        public class Marker
        {
            public GameObject markerObject;
            public Minimap.MarkerGroups markerType;
            public float markerDistance;
            public bool isActive;
            public bool inVision;
            public bool inLOS;
            public LayerMask markerLayerMask;
            public Material npcMarkerMaterial;
            public EnemySenses enemySenses;

            public Marker()
            {
                markerObject = null;
                markerType = Minimap.MarkerGroups.None;
                isActive = false;
                inVision = false;
                inLOS = false;
                markerLayerMask = 10;
                markerDistance = 0;
                npcMarkerMaterial = Minimap.buildingMarkerMaterial;
                enemySenses = null;
            }
        }

        // Creating an Instance (an Object) of the marker class to store and update specific object properties once initiated.
        public Marker marker = new Marker();

        //object general properties.
        public GameObject npcMarkerObject;
        public Material material;
        public Vector3 markerScale;
        public IEnumerator updateMarkerRoutine;
        public float frameTime;
        private bool startMarkerUpdates;

        private void Start()
        {
            MobilePersonNPC mobileNPC = GetComponentInParent<MobilePersonNPC>();
            DaggerfallEnemy mobileEnemy = GetComponentInParent<DaggerfallEnemy>();
            StaticNPC flatNPC = GetComponentInParent<StaticNPC>();

            //setup base npc marker object and properties.
            npcMarkerObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            Destroy(npcMarkerObject.GetComponent<Collider>());
            marker.npcMarkerMaterial = Minimap.updateMaterials(npcMarkerObject, Color.white, .5f);
            marker.markerObject = npcMarkerObject;
            marker.isActive = true;
            npcMarkerObject.name = "NPCMarker";
            npcMarkerObject.transform.SetParent(transform, false);
            npcMarkerObject.layer = 10;

            //check if player is inside or not, and then setup proper marker size.
            //This needs moved to update in some way, so it updates on entering a building/dungeon.
            if (GameManager.Instance.IsPlayerInside)
                markerScale = new Vector3(Minimap.indicatorSize, .01f, Minimap.indicatorSize);
            else
                markerScale = new Vector3(Minimap.indicatorSize, .01f, Minimap.indicatorSize);

            //set marker object scale.
            npcMarkerObject.transform.localScale = markerScale;

            //if friendly npc present, setup flat npc marker color, type, and activate marker object so iit shows on minimap.
            if (mobileNPC != null)
            {
                marker.markerType = Minimap.MarkerGroups.Friendlies;
                marker.npcMarkerMaterial.color = Color.green;
                npcMarkerObject.SetActive(false);
                npcMarkerObject.name = npcMarkerObject.name + " " + mobileNPC.NameNPC;
            }
            //if enemy npc present, setup flat npc marker color, type, and activate marker object so iit shows on minimap.
            else if (mobileEnemy != null)
            {
                marker.npcMarkerMaterial.color = Color.red;
                marker.markerType = Minimap.MarkerGroups.Enemies;
                npcMarkerObject.SetActive(false);
                marker.enemySenses = GetComponentInParent<DaggerfallEnemy>().GetComponent<EnemySenses>();
            }
            //if static npc present, setup flat npc marker color, type, and activate marker object so iit shows on minimap.
            else if (flatNPC != null)
            {
                marker.npcMarkerMaterial.color = Color.yellow;
                marker.markerType = Minimap.MarkerGroups.Resident;
                npcMarkerObject.SetActive(false);
            }
            else
            {
                marker.isActive = false;
            }      
        }

        void Update()
        {
            UnityEngine.Profiling.Profiler.BeginSample("NPC Scripts");
            float timePass =+ Time.deltaTime;

            //adjust how fast markers update to help potatoes computers. If above 60FPS, frame time to 60FPS update times. If below, knock it down to 30FPS update times.

            if (timePass > .1f)
            {
                timePass = 0;
                //if the marker is turned off compleetly, turn off marker object and stop any further updates.
                if (!marker.isActive)
                {
                    Debug.Log(npcMarkerObject.name + " | " + ObjectInView() + " | " + NPCinLOS());
                    marker.markerObject.SetActive(false);
                    return;
                }
                //if player has camera detect and realistic detection off, enable npc marker. This setting turns on all markers.
                else if (!Minimap.minimapControls.cameraDetectionEnabled && !Minimap.minimapControls.realDetectionEnabled)
                {
                    marker.markerObject.SetActive(true);
                    return;
                }
                //if marker is active, check if it is in view, and if so, turn on. If not, turn off.
                else if (Minimap.minimapControls.cameraDetectionEnabled && !Minimap.minimapControls.realDetectionEnabled)
                {
                    if (!ObjectInView() && marker.markerObject.activeSelf)
                        marker.markerObject.SetActive(false);
                    else if (ObjectInView() && !marker.markerObject.activeSelf)
                        marker.markerObject.SetActive(true);
                    return;
                }
                //if marker is active, check if it is in view and within detection radius, and if so, turn on.
                //If not and more than half the distance away, turn off marker.
                else if (Minimap.minimapControls.realDetectionEnabled)
                {
                    //if it is an active enemy, only show their icon when they are in actual line of sight of player.
                    //a hostile would actively try to mask their position until seen.                
                    if (marker.markerType == Minimap.MarkerGroups.Enemies)
                    {
                        if (marker.enemySenses.TargetInSight)
                            marker.markerObject.SetActive(true);
                        else
                            marker.markerObject.SetActive(false);
                    }
                    //else if friendly, show within radius.
                    else
                    {
                        if (NPCinLOS() && ObjectInView())
                        {
                            marker.markerObject.SetActive(true);
                        }
                        else if ((MarkerDistanceFromPlayer() > Minimap.minimapSensingRadius / 2) || !NPCinLOS())
                            marker.markerObject.SetActive(false);

                        //if (marker.markerObject.activeSelf && !ObjectInView() && MarkerDistanceFromPlayer() > Minimap.minimapSensingRadius / 2)
                        //marker.markerObject.SetActive(false);
                        //else if(!marker.markerObject.activeSelf && ObjectInView() && MarkerDistanceFromPlayer() < Minimap.minimapSensingRadius)
                        //marker.markerObject.SetActive(true);
                    }
                }
            }

            UnityEngine.Profiling.Profiler.EndSample();
        }       

        public bool NPCinLOS()
        {
            RaycastHit hit;
            Ray ray = new Ray(transform.position, GameManager.Instance.PlayerController.transform.position - transform.position);
            if (Physics.Raycast(ray, out hit, Minimap.minimapSensingRadius))
            {
                PlayerMotor playerCheck = hit.transform.GetComponent<PlayerMotor>();

                if(playerCheck != null)
                    marker.inLOS = true;
                else
                    marker.inLOS = false;
            }

            return marker.inLOS;
        }

        //gets npc/marker distance from player.
        public float MarkerDistanceFromPlayer()
        {
            if (marker.markerObject)
                marker.markerDistance = Vector3.Distance(transform.position, GameManager.Instance.MainCamera.transform.position);
            else
                marker.markerDistance = 0;

            return marker.markerDistance;
        }

        //gets npc/marker is within the camera view by using camera angle calcuations.
        public bool ObjectInView()
        {
            if (marker.markerObject)
                marker.inVision = GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(GameManager.Instance.MainCamera), marker.markerObject.GetComponent<MeshRenderer>().GetComponent<Renderer>().bounds);
            else
                marker.inVision = false;

            return marker.inVision;
        }

        //forces maker off or not.
        public static bool SetActive(bool isActive)
        {
            bool markerStatus = isActive;
            return markerStatus;
        }
    }
}
Here is ways of attaching it to objects at run time to make the scriptable object at run time bypassing the editor and mod builder.

Code: Select all

 
 //adds a new instance of mod script npcMarker to DFU scriptable Object Mobile NPCS
 npcMarker newNPCMarker = mobileNPC.gameObject.AddComponent<npcMarker>();
DistancetoPlayer = newNPCMarker.GetDistance();
You can always create a blank instance and run/grab what you need from it that way too.
However, this is not attached to any object and cannot do any object based class code work.
Used more for general code routines; this is not technically a scriptable object since it isn't attached to an object. It is a script class.

Code: Select all

npcMarker newNPCMarker =  new npcMarker();
Or, if you are creating custom objects that are suppose to use and run the script, you can create a blank game object to manipulate and attach it too.

Code: Select all

//creates an empty game object that can be manipulated however you want in the game scene space.
GameObject newScriptableObject =  new GameObject ();
//adds the class script to the newly created object. The script should run when added and do whatever it needs to the object.
npcMarker newNPCMarker = newScriptableObject .AddComponent<npcMarker>();
From my experience, you cannot trust things that run in editor through prefabs and editor classes will compile properly. It is best to figure out how the scripts work to create the same objects and classes at run time instead of creating complex mods in the editor to find they fall apart on compiling; this is what happened to the first version of the minimap mod, until I tracked down the dependency issues and created the code to make the objects at run time, instead of trying to compile them in the mod builder.
My Daggerfall Mod Github: l3lessed DFU Mod Github

My Beth Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

Post Reply