Construction System

AND… I got the entire thing to work, thank you for the brillian suggestion regarding the text color change @bixarrio. It made the “GetDescription()” function severely more complex (i.e: more lines of code), but as it turns out, it was quite simple (I’m sure it could’ve been more optimized). Until further testing down the line, it’s safe to assume now that this entire system works exactly as expected, although it was a hassle to make it work :slight_smile:

Now I can safely say the Construction system is done, until I get to the game design stage (i.e: outside of coding)

EDIT: IGNORE THIS ERROR - IT WAS JUST THERE BECAUSE I FORGOT TO ASSIGN MY MISSING SCRIPTS…!!!

hey @bixarrio - lol I know this is a closed topic, but… I accidentally got an NRE from one of the scripts we coded for the building system. Here’s what happened:

I placed a torch on the floor of the game, saved and quit and then tried returning to the game, and it won’t get the game to start. This is the error I got:

NullReferenceException: Object reference not set to an instance of an object
EasyBuildSystem.Features.Runtime.Player.PlayerBuildResourceConsumer.OnBuildingPartPlaced (EasyBuildSystem.Features.Runtime.Buildings.Part.BuildingPart buildingPart) (at Assets/Easy Build System/Features/Runtime/Player/PlayerBuildResourceConsumer.cs:41)
UnityEngine.Events.InvokableCall`1[T1].Invoke (T1 args0) (at <ba783288ca164d3099898a8819fcec1c>:0)
UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0) (at <ba783288ca164d3099898a8819fcec1c>:0)
EasyBuildSystem.Features.Runtime.Buildings.Manager.BuildingManager.PlaceBuildingPart (EasyBuildSystem.Features.Runtime.Buildings.Part.BuildingPart buildingPart, UnityEngine.Vector3 position, UnityEngine.Vector3 rotation, UnityEngine.Vector3 scale, System.Boolean createNewGroup) (at Assets/Easy Build System/Features/Runtime/Buildings/Manager/BuildingManager.cs:385)
EasyBuildSystem.Features.Runtime.Buildings.Manager.Saver.RPGCourseBuildingSaver.RestoreFromJToken (Newtonsoft.Json.Linq.JToken state) (at Assets/Project Backup/Scripts/BuildSystem/RPGCourseBuildingSaver.cs:68)
GameDevTV.Saving.JSONSaveableEntity.RestoreFromJToken (Newtonsoft.Json.Linq.JToken s) (at Assets/Project Backup/Scripts/Saving/JSON Saving System/JSONSaveableEntity.cs:51)
GameDevTV.Saving.JSONSavingSystem.RestoreFromToken (Newtonsoft.Json.Linq.JObject state) (at Assets/Project Backup/Scripts/Saving/JSON Saving System/JSONSavingSystem.cs:150)
GameDevTV.Saving.JSONSavingSystem+<LoadLastScene>d__1.MoveNext () (at Assets/Project Backup/Scripts/Saving/JSON Saving System/JSONSavingSystem.cs:33)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <ba783288ca164d3099898a8819fcec1c>:0)

Which basically leads to line 41 of the ‘PlayerBuildResourceConsumer.cs’ script, I’ll comment it on the script below (THE FOREACH LOOP DECLARATION LINE):

using EasyBuildSystem.Features.Runtime.Buildings.Manager;
using EasyBuildSystem.Features.Runtime.Buildings.Part;
using EasyBuildSystem.Features.Runtime.Buildings.Recipe;
using GameDevTV.Inventories;
using RPG.Skills;
using UnityEngine;

namespace EasyBuildSystem.Features.Runtime.Player {

public class PlayerBuildResourceConsumer : MonoBehaviour
{

    private Inventory inventory;
    private SkillExperience skillExperience;

    private void Awake() 
    {
        // cache the player Inventory
        inventory = Inventory.GetPlayerInventory();
        skillExperience = GameObject.FindWithTag("Player").GetComponent<SkillExperience>();
    }

    private void OnEnable() 
    {
        // Bind to the building manager
        BuildingManager.Instance.OnPlacingBuildingPartEvent.AddListener(OnBuildingPartPlaced);
    }

    private void OnDisable() 
    {
        // release the building manager
        BuildingManager.Instance.OnPlacingBuildingPartEvent.RemoveListener(OnBuildingPartPlaced);
    }

    public void OnBuildingPartPlaced(BuildingPart buildingPart) 
    {
        // a building was placed, consume the resources
        // 1. get the recipe from the 'BuildingPartRecipe'
        var buildingPartRecipe = buildingPart.GetComponent<BuildingPartRecipe>();
        // 2. Consume the ingredients
        foreach (var item in buildingPartRecipe.BuildingRecipe.GetIngredients()) // line 41
        {
            inventory.RemoveItem(item.Item, item.Amount);
        }
        // 3. Gain Construction Experience
        skillExperience.GainExperience(Skill.Construction, buildingPartRecipe.XPReward);
    }

}

}

Any ideas what went wrong?

before we close this topic, I want to hold it back because I’m attempting to integrate a free look camera to the construction system, now that I actually have a better idea of how to set one up :slight_smile: - I just want to keep it open in case I need help finding where to place the lines of code that prioritize the free look cameras above one another

Edit: And… yup, this needed to remain open, it’s literally about to turn into a trainwreck

hey @bixarrio - sorry to bother you again man, but… something weird is going on with this system. If you try to construct something with more than one item of what the construction requires, it flat out rejects to ever go green and always stays red. In other words, get rid of the extra stuff otherwise you can’t build (like… WHY?!)

How do I get rid of… well… this problem? :sweat_smile: - where have I went wrong? (I’m researching my code as we speak)

I’m guessing it has something to do with this function, in ‘RequiredIngredientsBuildingCondition.cs’:

    public override bool CheckPlacingCondition()
    {
        var buildingRecipe = GetComponent<BuildingPartRecipe>().BuildingRecipe;

        // Check if we have the required items in our inventory
        var inventory = Inventory.GetPlayerInventory();

        foreach (var craftingItem in buildingRecipe.GetIngredients()) 
        {
            // if (!HasConstructionLevel()) return false;
            if (!inventory.HasItem(craftingItem.Item, out int amount)) return false;
            if (craftingItem.Amount < amount) return false;
        }

        return true;

    }

specifically the ‘inventory.HasItem()’ line, in the if statement

Edit: Ahh… Nevermind, fixed it:

if (!inventory.HasItem(craftingItem.Item, out int amount) || amount < buildingItem.Amount) return false;
// don't need "if (craftingItem.Amount < amount) return false" anymore...

the final problem in your DMs though, about hiding the recipe cost for stuff that costs $0 to make, still exists though :sweat_smile:

heya @bixarrio - OK I honestly thought this system was done for, but… the Editing and Destruction systems don’t work. Yes, I do close the UI when I go into either mode, but it doesn’t highlight anything, even if my player can see it. You still have a copy of my project, right? If so, can you please have a look through? I managed to identify the starting part of the problem as follows:

        public void SelectDestroyMode()
        {
            Debug.Log("Destruction Mode Entered");
            BuildingPlacer.Instance.ChangeBuildMode(BuildingPlacer.BuildMode.DESTROY);
            CloseMenu();
        }

This is a function in ‘UICircularBuildingMenu.cs’

The Debugger works, and placing one in ‘BuildingPlacer.Instance.ChangeBuildMode()’ works as well. The problem is… I can no longer highlight the part I want to destroy, and the same problem goes for editing. Can you please have a look at it? It would really mean a lot to me :slight_smile:

I asked the developer about this 2 days ago and still no response… (Please help)

All the changes I have done so far were documented. I did not change anything behind the scenes

As soon as you see this message, please let me know :slight_smile:

Edit: I figured this out too. I recently had my Terrain’s LayerMask set to “Terrain”, and the system is not programmed to work with that mask. I contacted Andrew about it, but… it’ll probably take some time for him to respond :sweat_smile: (I asked him how to get the edit and destroy functions to work on different layers too)

Edit 2: With a little bit (OK a lot) of fiddling around, I fixed it. Just let the Raycast camera detect both “Terrain” AND “Default”. Another day, another major problem to encounter :slight_smile:

Edit: I developed an algorithm as well which (visibility-wise) hides any construction stuff in your face that might block the view between the player and the camera, something on the fly to ensure that you can see your home. It’s not as smooth as professional games make it seem to be, but for the moment it works. Here’s the algorithm, for anyone who needs it:

using System.Collections.Generic;
using UnityEngine;

public class BuildingVisibilityController : MonoBehaviour
{
    public LayerMask buildingLayerMask;
    public float visibilityDistanceThreshold = 5f;

    public List<Renderer> visibleRenderers = new List<Renderer>();

    private void Update()
    {
        // Iterate over all currently visible renderers and check if they are still visible
        for (int i = 0; i < visibleRenderers.Count; i++)
        {
            Renderer renderer = visibleRenderers[i];
            if (renderer != null)
            {
                float distance = Vector3.Distance(transform.position, renderer.bounds.center);
                if (distance >= visibilityDistanceThreshold)
                {
                    renderer.enabled = true; // Set it to be visible again
                    visibleRenderers.RemoveAt(i); // Remove from the list
                    i--; // Decrement the index to account for removed item
                }
            }
        }

        // Perform a new raycast to find building parts within the visibility threshold
        RaycastHit[] hits = Physics.RaycastAll(transform.position, transform.forward, Mathf.Infinity, buildingLayerMask);
        foreach (RaycastHit hit in hits)
        {
            Renderer renderer = hit.collider.GetComponent<Renderer>();
            if (renderer != null && !visibleRenderers.Contains(renderer))
            {
                float distance = hit.distance;
                if (distance < visibilityDistanceThreshold)
                {
                    renderer.enabled = false; // Set it to be invisible
                    visibleRenderers.Add(renderer); // Add it to the list of visible renderers
                }
            }
        }
    }
}

It works by setting the PART of your house as whatever layer you want to hide (for me personally, it’s “Obstacles”), and doing the same for the camera, and then it adds it to a list. Once you’re far enough from that part, it will delete it off the list and re-render it. It could be a little annoying for when you’re coming to the camera and need to see what’s ahead of you, but I haven’t made this a whole lot mature as of yet. For now, Enjoy :slight_smile:

if you want to disable the entire gameObject instead (i.e: it doesn’t exist in the game), here’s the script:

using System.Collections.Generic;
using UnityEngine;

// Find a way to deactivate this script when the player is in Construction, Editing, or Destruction Mode

public class BuildingVisibilityController : MonoBehaviour
{
    [Header("This script is responsible for hiding any obstacles\nbetween the camera and the player character\n (You still need to find a way to get it to work with Obstacle-Layers, as Construction fails right there)")]
    public LayerMask buildingLayerMask;
    public float visibilityDistanceThreshold = 5f;

    public List<GameObject> visibleObjects = new List<GameObject>();

    private void Update()
    {
        // Iterate over all currently visible objects and check if they are still visible
        for (int i = 0; i < visibleObjects.Count; i++)
        {
            GameObject obj = visibleObjects[i];
            if (obj != null)
            {
                float distance = Vector3.Distance(transform.position, obj.transform.position);
                if (distance >= visibilityDistanceThreshold)
                {
                    obj.SetActive(true); // Set it to be visible again
                    visibleObjects.RemoveAt(i); // Remove from the list
                    i--; // Decrement the index to account for removed item
                }
            }
        }

        // Perform a new raycast to find building parts within the visibility threshold
        RaycastHit[] hits = Physics.RaycastAll(transform.position, transform.forward, Mathf.Infinity, buildingLayerMask);
        foreach (RaycastHit hit in hits)
        {
            GameObject obj = hit.collider.gameObject;
            if (obj != null && !visibleObjects.Contains(obj))
            {
                float distance = hit.distance;
                if (distance < visibilityDistanceThreshold)
                {
                    obj.SetActive(false); // Set it to be invisible
                    visibleObjects.Add(obj); // Add it to the list of visible objects
                }
            }
        }
    }
}

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Alright back to this lengthy topic. As of recent times, I noticed the edit mode for my construction system, whilst it works quite well, has a few flaws. I fixed most of them, but there’s only one bug that exists to be fixed, and after that the EBS can be a certified possible integration for the architecture of this course :slight_smile:

Recently, I introduced a “Right Click to Cancel Construction” function (in ‘Update()’. I figured it’s a simple if statement that won’t hurt anyone), and what I want to do, is if you’re holding a part in Edit Mode (which, you most likely are), if you cancel it, you want to place that part where it belonged

I believe a struct would work for this quite well, but… what are the basic steps to do this?

Privacy & Terms