How to scale only one game object in the AR scene in Unity? - c#

so I have like 5 game object in my scene but I only scale each of them separately. However when I try to do that all of them start scaling simultaneously. Also, I have a placement indicator that would be used to instantiate the object on the plane. It seems that instead of the object itself, the placement indicator is the one that gets scaled. How should I fix that?
I have tried deactivating the placement indicator but did not work.
Here is the code for instantiating objects:
I limited the obj number to 5.
I use this script instead of the usual "PlaceonPlane" script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.Experimental.XR;
using UnityEngine.UI;
using UnityEngine.XR.ARSubsystems;
public class ARTaptoPlaceObject : MonoBehaviour
{
private ARSessionOrigin arOrigin;
GameObject spawnedobj;
public GameObject placementIndicator;
private ARRaycastManager arRaycast;
public Pose placementPose;
public UIContoller sc;
public bool placementPoseIsValid = false;
private int count;
private string valu;
string prefabs;
void Start()
{
arOrigin = FindObjectOfType<ARSessionOrigin>();
arRaycast = FindObjectOfType<ARRaycastManager>();
count = 0;
}
// Update is called once per frame
void Update()
{
UpdatePlacementPose();
UpdatePlacementIndicator();
for (var i = 0; i < Input.touchCount; ++i)
{
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
if (placementPoseIsValid && Input.GetTouch(i).tapCount == 2)
{
PlaceObject();
}
}
}
}
public void PlaceObject()
{
if (count <= 4)
{
if (sc.objectToPlace != null)
{
spawnedobj = Instantiate(sc.objectToPlace, placementPose.position, placementPose.rotation);
arOrigin.MakeContentAppearAt(spawnedobj.transform, spawnedobj.transform.position, spawnedobj.transform.rotation);
count++;
}
}
else
{
placementIndicator.SetActive(false);
}
}
private void UpdatePlacementIndicator()
{
if (placementPoseIsValid && count <= 4 && sc.active == false)
{
placementIndicator.SetActive(true);
placementIndicator.transform.SetPositionAndRotation(placementPose.position, placementPose.rotation);
}
else
{
placementIndicator.SetActive(false);
}
}
private void UpdatePlacementPose()
{
var screenCenter = Camera.current.ViewportToScreenPoint(new Vector3(0.5f, 0.5f));
var hits = new List<ARRaycastHit>();
arRaycast.Raycast(screenCenter, hits, UnityEngine.XR.ARSubsystems.TrackableType.Planes);
placementPoseIsValid = hits.Count > 0;
if (placementPoseIsValid)
{
placementPose = hits[0].pose;
var cameraForward = Camera.current.transform.forward;
var cameraBearing = new Vector3(cameraForward.x, 0, cameraForward.z).normalized;
placementPose.rotation = Quaternion.LookRotation(cameraBearing);
}
}
}
and here is the Scaler script that's attached to the button that would scale the object.
public class Scaler : MonoBehaviour
{
public UIContoller uc;
public ARTaptoPlaceObject ap;
private GameObject ReferenceToScale;
public void OnValueChange()
{
ReferenceToScale = (UnityEngine.GameObject)Resources.Load(uc.s_count, typeof(GameObject));
Vector3 t = ReferenceToScale.transform.localScale;
Vector3 scaleValue = t * 1.1f;
ReferenceToScale.transform.localScale = scaleValue;
}
Also the "objectToPlace" itself is in the "UI.Controller" script as I could not view it in the scene when it was in the "ARTaptoPlace" script

Related

Keep track of state of GameObject after destroying it

I have a ScriptableObject called WeaponInfo that keeps track of different information about weapons that doesn't change (magazine max, rate of fire, degradable bool, etc.) and this class also has a reference to a base weapon Prefab that inherits from Monobehaivior. This prefab is what is instantiated and then given a reference to the WeaponInfo scriptable object. My problem with this setup is as follows:
I want the weapon to have a durability value that decreases with each use. I can't put this in the ScriptableObject since ScriptableObjects data is "static", and changing it is not a good idea. I also can't put it inside the GameObject Prefab itself, because every time the player unequips then reequips the weapon, the durability value is reset to its default state.
I looked around the internet for solutions, but no one has a similar example to my setup where the weapon is unequipped by deleting it from the scene. I also want the ability to store this weapon in a chest. Originally I was gonna do this by storing a reference to the ScriptableObject, but I won't be able to save the durability.
What is the best approach to solving this issue? If anyone wants, I can share code, but I wanted to keep the post simple since my issue is conceptual more than it is syntax.
EDIT: Here is the code for my WeaponInfo class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Weapon Item", menuName = "Inventory/Weapon Item")]
public class WeaponItem : Item
{
[Header("Game Object Info")]
public GameObject WeaponPrefab;
public Vector3 DefaultRotation;
public Vector3 DefaultScale;
[SerializeField]
public BulletItem Bullet;
[Header("Weapon Info")]
public bool Automatic;
[SerializeField]
public int Magazine_Max;
[SerializeField]
public int Ammo_Max;
[SerializeField]
public int Reloading_Time;
[SerializeField]
public float ROF;
public bool degradable;
public override void Use()
{
base.Use();
//oldWeapon is of type WeaponItem
var oldWeapon = WeaponSwapper.Instance.SwapWeapon(this);
Inventory.Instance.AddItem(oldWeapon);
Inventory.Instance.RemoveItem(this);
}
}
And here's the code attached to my weapon prefab:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AmmoWeapon : MonoBehaviour
{
[System.NonSerialized]
public WeaponItem WeaponInfo;
private Inventory inventory;
protected float reloadingTimer;
protected bool reloading;
[SerializeField]
public int Magazine_Current;
[SerializeField]
public int Ammo_Current;
public int durability;
private float elapsedTime = 0;
void Start()
{
inventory = Inventory.Instance;
inventory.OnInventoryChangedCallback += UpdateAmmo;
reloadingTimer = 0;
reloading = false;
LoadAmmo();
}
//this method loads a clip into the magazine
private void LoadAmmo()
{
var totalAmmo = inventory.GetCount(WeaponInfo.Bullet);
if(totalAmmo >= WeaponInfo.Magazine_Max)
{
Magazine_Current = WeaponInfo.Magazine_Max;
Ammo_Current = totalAmmo - WeaponInfo.Magazine_Max;
} else
{
Magazine_Current = totalAmmo;
Ammo_Current = 0;
}
}
//callback for whenever ammo is added to the inventory.. also used to reflect the ammo in the UI
private void UpdateAmmo()
{
var totalAmmo = inventory.GetCount(WeaponInfo.Bullet);
Ammo_Current = totalAmmo;
if (Ammo_Current <= Magazine_Current)
{
Magazine_Current = Ammo_Current;
Ammo_Current = 0;
}
else
Ammo_Current -= Magazine_Current;
}
void Update()
{
//Automatic
if (Input.GetMouseButton(0) && WeaponInfo.Automatic)
{
Fire();
}
//Manual
if (Input.GetMouseButtonDown(0) && !WeaponInfo.Automatic)
{
Fire();
}
//Set reloading flag after a certain amount of time passes
if (reloading)
{
reloadingTimer += Time.deltaTime;
if (reloadingTimer > WeaponInfo.Reloading_Time)
{
reloadingTimer = 0;
reloading = false;
}
}
elapsedTime += Time.deltaTime;
Mathf.Clamp(elapsedTime, 0, WeaponInfo.ROF);
}
protected void Reload()
{
if (reloading)
return;
reloading = true;
reloadingTimer = 0;
LoadAmmo();
}
protected void Fire()
{
if (Magazine_Current == 0)
Reload();
if (!CanFire())
return;
var target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
var bulletPrefab =
Instantiate(WeaponInfo.Bullet.BulletPrefab, transform.position, Quaternion.identity);
var bulletScript = bulletPrefab.GetComponent<Bullet>();
var spriteRenderer = bulletPrefab.GetComponent<SpriteRenderer>();
spriteRenderer.sprite = WeaponInfo.Bullet.Sprite;
bulletScript.BulletInfo = WeaponInfo.Bullet;
bulletScript.SetDirection(target - transform.position);
elapsedTime = 0;
Magazine_Current--;
inventory.RemoveItem(WeaponInfo.Bullet);
//ISSUE IS HERE. durability is reset to initial value whenever this gameobject is initialized again
if (WeaponInfo.degradable)
{
durability--;
if (durability == 0)
{
MessageHandler.Instance.DisplayMessage(WeaponInfo.ItemName + " degraded completely.");
Destroy(gameObject);
}
}
}
protected bool CanFire()
{
if (reloading || elapsedTime < WeaponInfo.ROF || Magazine_Current == 0 || !inventory.Contains(WeaponInfo.Bullet))
return false;
return true;
}
}
And finally, here's the snippet where the weapon is instantiated and attached to my weapon rod:
public WeaponItem SwapWeapon(WeaponItem NewWeapon)
{
DestroyAllChildren();
var previousWeapon = CurrentWeapon;
CurrentWeapon = NewWeapon;
InstantiateAndAttachWeapon(CurrentWeapon);
return previousWeapon;
}
private void InstantiateAndAttachWeapon(WeaponItem weapon)
{
WeaponPrefab = Instantiate(weapon.WeaponPrefab);
var weaponScript = WeaponPrefab.GetComponent<AmmoWeapon>();
weaponScript.WeaponInfo = weapon;
var sprite = WeaponPrefab.GetComponent<SpriteRenderer>();
sprite.sprite = weapon.Sprite;
Debug.Log(CurrentWeapon.DefaultRotation);
WeaponPrefab.transform.parent = transform;
WeaponPrefab.transform.localRotation = Quaternion.Euler(
CurrentWeapon.DefaultRotation);
WeaponPrefab.transform.localScale = weapon.DefaultScale;
WeaponPrefab.transform.localPosition = Vector3.zero;
SetAmmoScript(weaponScript);
}

Cant seem to get the player position with Vector3 with unity

How would i get the player position so i can drop the item about 5 units infrount of the player so they do not imeadently pick up the item after they drop it and how do i get the Vector3 position of the player as well tried in many ways and i still cant get it to work i am trying to do it here:
public void DropItem()
{
if(slotsItem)
{
slotsItem.transform.parent = null;
slotsItem.gameObject.SetActive(true);
slotsItem.transform.position = Vector3.lastpos;
}
then here is the full code as well
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class Slot : MonoBehaviour
{
public Item slotsItem;
public Transform player;
Sprite defaultSprite;
Text amountText;
public void CustomStart()
{
defaultSprite = GetComponent<Image>().sprite;
amountText = transform.GetChild(0).GetComponent<Text>();
this.player = GameObject.FindWithTag("Player").transform;
Vector3 lastpos = player.position;
}
public void DropItem()
{
if(slotsItem)
{
slotsItem.transform.parent = null;
slotsItem.gameObject.SetActive(true);
slotsItem.transform.position = Vector3.lastpos;
}
}
public void CheckForItem()
{
if(transform.childCount > 1)
{
slotsItem = transform.GetChild(1).GetComponent<Item>();
GetComponent<Image>().sprite = slotsItem.itemSprite;
if(slotsItem.amountInStack > 1)
amountText.text = slotsItem.amountInStack.ToString();
}
else
{
slotsItem = null;
GetComponent<Image>().sprite = defaultSprite;
amountText.text = "";
}
}
}
What is Vector3.lastpos supposed to be?
I think you rather want to store a field in the class
private Vector3 lastpos;
assign this field in
public void CustomStart()
{
...
lastpos = player.position;
}
and then later use it like
public void DropItem()
{
...
slotsItem.transform.position = lastpos;
...
}
though the question is why not simply use the current position
slotsItem.transform.position = player.position;

How can I apply changes for the speed variable also in the Update at run time?

It's working fine in the Start :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
[Header("Objects To Move")]
public Transform objectToMovePrefab;
public int numberOfObjectsToMove = 1;
[Header("Delay At Start")]
public bool useDelay = false;
public bool randomDelayTime = false;
public float delayTime = 3f;
[Header("Movement Speed")]
public float speed = 3f;
public bool randomSpeed = false;
//with this approach, you use GameObjects to represent your waypoints
//(they can be empty if you want the waypoint to be invisible)
[Header("Waypoints")]
[SerializeField] private List<Transform> waypoints;
private List<WaypointsFollower> waypointsFollowers;
[Header("LineRenderer")]
public LineRenderer lineRenderer;
private bool useLineRenderer = true;
private List<Vector3> lineRendererPositions;
[SerializeField] int lineRendererNumOfPositions;
private void Start()
{
for (int i = 0; i < numberOfObjectsToMove; i++)
{
var parent = GameObject.Find("Moving Object Parent");
var objectToMove = Instantiate(objectToMovePrefab, parent.transform);
objectToMove.name = "Platfrom";
}
foreach (GameObject mn in GameObject.FindGameObjectsWithTag("Moving Object"))
{
waypointsFollowers.Add(mn.GetComponent<WaypointsFollower>());
}
StartCoroutine(SendObjectToMove());
}
public int Count => lineRendererPositions.Count;
public Vector3 GetWaypoint(int index)
{
return lineRendererPositions[index];
}
private void Update()
{
if (useLineRenderer && lineRenderer.positionCount > 0 && CurvedLineRenderer.linesSet)
{
lineRendererPositions = GetLinePointsInWorldSpace();
lineRendererNumOfPositions = GetLinePointsInWorldSpace().Count;
foreach(Transform waypoint in waypoints)
{
lineRendererPositions.Add(waypoint.position);
}
useLineRenderer = false;
}
}
private IEnumerator SendObjectToMove()
{
foreach (GameObject mn in GameObject.FindGameObjectsWithTag("Moving Object"))
{
WaypointsFollower waypointsFollower = mn.GetComponent<WaypointsFollower>();
if (useDelay)
{
if (randomDelayTime)
{
yield return new WaitForSeconds(Random.Range(1, 5));
}
else
{
yield return new WaitForSeconds(delayTime);
}
}
if (randomSpeed)
{
waypointsFollower.speed = Random.Range(1, 100);
}
else
{
waypointsFollower.speed = speed;
}
if (waypoints.Count > 0 || lineRendererPositions.Count > 0)
{
waypointsFollower.go = true;
}
else
{
waypointsFollower.go = false;
}
}
}
List<Vector3> GetLinePointsInWorldSpace()
{
var pointsToMove = new Vector3[lineRenderer.positionCount];
//Get the positions which are shown in the inspector
lineRenderer.GetPositions(pointsToMove);
//the points returned are in world space
return pointsToMove.ToList();
}
}
Then on each moving object cloned prefab I added this script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WaypointsFollower : MonoBehaviour
{
[SerializeField] private Waypoints waypoints;
public float speed = 5f;
[SerializeField] private float waypointDistanceThreshold = 0.1f;
[SerializeField] private bool goBack = false;
public bool go = false;
private int waypointIndex = 0;
private void Start()
{
waypoints = GameObject.Find("Waypoints").GetComponent<Waypoints>();
}
void Update()
{
if (go && waypoints.Count > 0)
{
Vector3 waypoint = waypoints.GetWaypoint(waypointIndex);
//movement
float distance = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, waypoint, distance);
//check if we've reached the waypoint
float threshold = waypointDistanceThreshold; //how close is considered having reached the waypoint
if (Vector3.Distance(transform.position, waypoint) < threshold)
{
//wraps back to 0 when we reach last waypoint
if (goBack)
{
waypointIndex = (waypointIndex + 1) % waypoints.Count;
}
else
{
if (waypointIndex != waypoints.Count - 1)
waypointIndex = waypointIndex + 1;
}
}
}
}
}
I can control the speed of each Follower individual but I also want to control the speed of all the Followers from the main Waypoints script and for now I can change the speed in the Inspector and it will affect the changes only in the Start() when starting the game.
How can I make that it will change the speed of all the Followers also in the Update at run time ?
I created a List of all the Followers :
foreach (GameObject mn in GameObject.FindGameObjectsWithTag("Moving Object"))
{
waypointsFollowers.Add(mn.GetComponent<WaypointsFollower>());
}
but not sure how to apply the speed changes in the Update ? Looping in the Update over the Followers each frame is too expensive I guess.
I am not completely sure what you would like to achieve. Well, I know you want to increase the variable Speed on a list of objects that have the component WaypointsFollower(). I suppose what I am confused about is when you want to do this. If Update() is too frequent, what determines when the speed should change?
In your original code, you are using the GameObject.FindGameObjectsWithTag("Moving Object")) too frequently. Only do this once in Awake() or Start(). If any new objects are instantiated, then dynamically add them to the list. Using the FindGameObjectsWithTag frequently can get very expensive. What I am referring to is that you are already storing them, but then in the method SendObjectToMove() you are using it again. Is there a reason you need to call it again in that IEnumartor?
And if you are instantiating these objects on this line
for (int i = 0; i < numberOfObjectsToMove; i++)
{
var parent = GameObject.Find("Moving Object Parent");
var objectToMove = Instantiate(objectToMovePrefab, parent.transform);
objectToMove.name = "Platfrom";
}
Then just store the reference after instantiating them instead of using the FindGameObjectsWithTag.
Here is a very condensed version of your code.
private List<WaypointsFollower> waypointsFollowers;
private void Start()
{
foreach (GameObject mn in GameObject.FindGameObjectsWithTag("Moving Object"))
{
waypointsFollowers.Add(mn.GetComponent<WaypointsFollower>());
}
StartCoroutine(SendObjectToMove());
}
private IEnumerator SendObjectToMove()
{
foreach (WaypointsFollower follwer in waypointsFollowers)
{
// continue your code, but use follower instead of the other object
// you are already storing these objects, so why not reuse them?
}
}
private void UpdateSpeed(float speed)
{
foreach (WaypointsFollower follwer in waypointsFollowers)
{
follower.speed = speed
}
}
I am only using the FindGameObjectsWithTag once in Start() then reusing the list waypointsFollowers it is adding to. I added a function UpdateSpeed that will take a float as a parameter and update every object in the list.

Why only from the second door the door close ? The first one stay opened

The first script and the second both attached to the doors.
In this case I have 12 doors.
No matter what the first door of the 12 the player controller or a NPC enter the door it will open but stay opened. The next door the player controller or the NPC will enter will open and then also will be closed and then all the doors.
But each time the first door never close. It's working only from the second door each time when running the game.
On this script HoriDoorManager I'm using public static flag exitedDoor and set it to true inside the OnTriggerExit:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class HoriDoorManager : MonoBehaviour
{
public static bool exitedDoor = false;
private bool doorLockState;
private List<DoorHori> doors = new List<DoorHori>();
private void Start()
{
if (transform.parent != null)
{
Transform parent = transform.parent;
var children = parent.GetComponentsInChildren<Transform>();
if(children != null)
{
foreach (Transform door in children)
{
if (door.name == "Door_Left" || door.name == "Door_Right")
doors.Add(door.GetComponent<DoorHori>());
}
}
}
}
void OnTriggerEnter()
{
if (doorLockState == false)
{
if (doors != null)
{
for(int i =0; i < doors.Count; i++)
{
doors[i].OpenDoor();
}
}
}
}
private void OnTriggerExit(Collider collide)
{
if (doorLockState == false)
{
exitedDoor = true;
}
}
public void ChangeLockState(bool lockState)
{
doorLockState = lockState;
}
}
In the second script I'm checking if the flag exitedDoor is true and then start closing the door: Inside the method WaitToClose:
using UnityEngine;
using System.Collections;
public class DoorHori : MonoBehaviour
{
public float translateValue;
public float easeTime;
public OTween.EaseType ease;
public float waitTime;
private Vector3 StartlocalPos;
private Vector3 endlocalPos;
private void Start()
{
StartlocalPos = transform.localPosition;
gameObject.isStatic = false;
}
public void OpenDoor()
{
OTween.ValueTo(gameObject, ease, 0.0f, -translateValue, easeTime, 0.0f, "StartOpen", "UpdateOpenDoor", "EndOpen");
GetComponent<AudioSource>().Play();
}
private void UpdateOpenDoor(float f)
{
Vector3 pos = transform.TransformDirection(new Vector3(1, 0, 0));
transform.localPosition = StartlocalPos + pos * f;
}
private void UpdateCloseDoor(float f)
{
Vector3 pos = transform.TransformDirection(new Vector3(-f, 0, 0));
transform.localPosition = endlocalPos - pos;
}
private void EndOpen()
{
endlocalPos = transform.localPosition;
StartCoroutine(WaitToClose());
}
private IEnumerator WaitToClose()
{
if (HoriDoorManager.exitedDoor == true)
{
yield return new WaitForSeconds(waitTime);
OTween.ValueTo(gameObject, ease, 0.0f, translateValue, easeTime, 0.0f, "StartClose", "UpdateCloseDoor", "EndClose");
GetComponent<AudioSource>().Play();
}
}
}
Try changing the OnTriggerEnter to this in your first script.
private void OnTriggerEnter(Collider collide){
if (doorLockState == false)
{
if (doors != null)
{
for(int i =0; i < doors.Count; i++)
{
doors[i].OpenDoor();
}
}
}
}
You are not using unity's defined method when you remove the parameters of the method, so it is no longer referencing the same OnTriggerEnter.
It can then also be used to check what is triggering the on enter flag, because I am assuming you don't want any collisions to trigger this logic.

Instantiate inside nested for loops does not do what it is intended to

I have been working on a minecraft clone game in Unity 2017.3 and C# and i started all right but when i worked on the super flat world generation system there began a problem. Inside the World mono behaviour, in the Generate void, there are three nested for loops which should generate a new dirt block on every possible position between 0 and 5
But it only makes a line of dirt block stretching along the Z axis.
Here is the code for PlaceableItem(attached to dirtPrefab) and World(attached to World)
Placeable Item class
using UnityEngine;
using System.Collections;
public class PlaceableItem : MonoBehaviour
{
public string nameInInventory = "Unnamed Block";
public int maxStack = 64;
public bool destructible = true;
public bool explodesOnX = false;
public bool abidesGravity = false;
public bool isContainer = false;
public bool canBeSleptOn = false;
public bool dropsSelf = false;
private Rigidbody rb;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (abidesGravity) {
rb.useGravity = true;
} else {
rb.useGravity = false;
}
}
private void OnDestroy()
{
if (dropsSelf) {
//Drop this gameObject
}
}
public void Explode() {
if (!explodesOnX)
return;
}
public void OnMouseDown()
{
if (isContainer && !canBeSleptOn) {
//Open the container inventory
} else if (canBeSleptOn && !isContainer) {
//Make the character sleep on the item
}
}
private void OnMouseOver()
{
if (Input.GetMouseButtonDown(1) && destructible) {
Destroy(gameObject);
}
}
}
World class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class World : MonoBehaviour {
public GameObject dirtPrefab;
public bool generateAutomatically = true;
// Use this for initialization
void Start () {
if (generateAutomatically) {
Generate();
}
}
// Update is called once per frame
void Update () {
}
void Generate() {
for (int x = 0; x <= 5; x++) {
for (int y = 0; y <= 5; y++) {
for (int z = 0; z <= 5; z++) {
Instantiate(dirtPrefab, new Vector3(x, y, z), Quaternion.identity);
}
}
}
}
void RemoveAllBlocks() {
foreach (PlaceableItem placeableItem in GetComponentsInChildren<PlaceableItem>()) {
Destroy(placeableItem.gameObject);
}
}
}
Thanks in advance, fellow developers!
Hope this is not a stupid question!
[SOLVED] I realised i had a recttransform somehow attached to my dirt prefab. Removing it and adding a transform instead helped.

Categories