HI, For my Game I would like to add one weapon at a time. is there something I can look at for a reference? I know I can be done just need to add this in the inventory and as a weapon, just a little confused on how to do that.
There are a few approaches you can take to do this.
Here’s the direction I would take:
First, in WeaponInfo, we need a sprite for an icon for the weapon to place in the ActiveInventory. So I simply added
public Sprite sprite;
to Weapon Info (you’ll also need to add UnityEngine.UI; to your using clauses.)
Next, we need to add a method to InventorySlot.cs that allows us to assign a WeaponInfo
public void AssignWeaponInfo(WeaponInfo weaponInfo)
{
this.weaponInfo = weaponInfo;
transform.GetChild(1).GetComponent<Image>().sprite = weaponInfo.sprite;
}
With these two additions, you should be able to dynamically add a Weapon to a slot, perhaps in a pickup/
The tricky part is figuring out where the weapon should go when we pick it up… we need to find an available slot… So a method in ActiveInventory may help with that:
InventorySlot GetEmptyInventorySlot()
{
foreach (Transform child in transform)
{
if (child.TryGetComponent(out InventorySlot inventorySlot)) return inventorySlot;
}
return null;
}
HI Brian,
I did what you said I also added using UnityEngine.UI; into my InventorySlot.cs otherwise I got an error.
I will keep working on this and let you know.
Yes, anything that references an Image will need the using UnityEngine.UI; added.
Also so I don’t forget to mention I added the Inventory images to the new Weapons prefab sprites (I assume that’s what I needed to do).
Yes, that way the scripts can copy the sprite over to the InventorySlot
Am I suppose to add another script for pick up weapons? or expand on the one we already have?
You can do either. If you use the Pickup we use for coins/stamina, etc, then you can take advantage of the code that moves the items to the player. You can add a new value to the enum for the type, and in the switch/case statement, get the player’s ActiveWeaponScript with GetComponent, use that to get an empty InventorySlot, and then call the InventorySlot’s AssignWeaponInfo.
ok. when do we call the GetEmptyInventorySlot() function in the ActiveInventory.cs class?
When you pick up the item.
- Get an empty inventory slot by calling the activeInventory.GetEmptyInventorySlot() (you’ll need a reference to the ActiveInventory
- If the return from GetEmptyInventorySlot() is not null, call the inventorySlot’s AssignWeaponInfo.
I think I’m just not getting what you are saying
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ActiveInventory : Singleton<ActiveInventory>
{
private int activeSlotIndexNum = 0;
private PlayerActions playerActions;
protected override void Awake()
{
base.Awake();
playerActions = new PlayerActions();
}
private void Start()
{
playerActions.Inventory.Keyboard.performed += ctx =>
ToggleActiveSlot((int)ctx.ReadValue<float>());
}
private void OnEnable()
{
playerActions.Enable();
}
public void EquipStartingWeapon()
{
//Give the player the sword in the inventory
ToggleActiveHighlight(0);
}
private void ToggleActiveSlot(int numPressed)
{
//Need to match up number pressed to the actual inventory index {0-4} [1-5]that is why we -->(- 1)
ToggleActiveHighlight(numPressed -1);
}
private void ToggleActiveHighlight(int indexNum)
{
activeSlotIndexNum = indexNum;
//First set all agme objects for the inventory slots off
foreach (Transform inventorySlot in this.transform)
{
inventorySlot.GetChild(0).gameObject.SetActive(false);
}
//Now set the one that we want to be active
this.transform.GetChild(indexNum).GetChild(0).gameObject.SetActive(true);
ChangeActiveWeapon();
}
private void ChangeActiveWeapon()
{
if(PlayerHealth.Instance.IsDead){return;}
if(ActiveWeapon.Instance.CurrentActiveWeapon != null)
{
Destroy(ActiveWeapon.Instance.CurrentActiveWeapon.gameObject);
}
Transform childTransform = transform.GetChild(activeSlotIndexNum);
InventorySlot inventorySlot = childTransform.GetComponentInChildren<InventorySlot>();
WeaponInfo weaponInfo = inventorySlot.GetWeaponInfo();
// if nothing is in an inventory slot
if (weaponInfo == null)
{
//return null
ActiveWeapon.Instance.WeaponNull();
return;
}
GameObject weaponToSpawn = weaponInfo.weaponPrefab;
//Now Instantiate the new weapon
GameObject newWeapon = Instantiate(weaponToSpawn, ActiveWeapon.Instance.transform.position, Quaternion.identity);
// we are cleaning out first the rotation of a prior wepon so any weapon does not spin on a certain point.
ActiveWeapon.Instance.transform.rotation = Quaternion.Euler(0,0,0);
//now make the weapon a child of our active weapon.
newWeapon.transform.parent = ActiveWeapon.Instance.transform;
ActiveWeapon.Instance.NewWeapon(newWeapon.GetComponent<MonoBehaviour>());
}
public InventorySlot GetEmptyInventorySlot() // NEW for active weapon.
{
foreach (Transform child in transform)
{
//Debug.Log("inventorySlot");
if (child.TryGetComponent(out InventorySlot inventorySlot)) return inventorySlot;
Debug.Log(inventorySlot);
}
return null;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;// NEW for active weapon.
public class InventorySlot : MonoBehaviour
{
[SerializeField] private WeaponInfo weaponInfo;
public WeaponInfo GetWeaponInfo()
{
return weaponInfo;
}
public void AssingWeaponInfor(WeaponInfo weaponInfo) // NEW for active weapon.
{
this.weaponInfo = weaponInfo;
transform.GetChild(1).GetComponent<Image>().sprite = weaponInfo.sprite;
Debug.Log(weaponInfo.weaponRange);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;// NEW for active weapon.
[CreateAssetMenu(menuName = "New Weapon")]
public class WeaponInfo : ScriptableObject
{
public GameObject weaponPrefab;
public float weaponCooldown;
public int weaponDamage;
public float weaponRange;
public Sprite sprite; // NEW for active weapon.
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PickUp : MonoBehaviour
{
private enum PickUpType
{
GoldCoin,
StaminaGlobe,
HealthGlobe,
Sword,
}
[SerializeField] private PickUpType pickUpType;
[SerializeField] AudioClip pickupClip;
[SerializeField] private float pickUpDistance = 5f;
[SerializeField] private float moveSpeed = 3f;
[SerializeField] private float accelarationRate = .4f;
[SerializeField] private AnimationCurve animCurve;
[SerializeField] private float heightY = 1.5f;
[SerializeField] private float popDuration = 1f;
ActiveInventory activeInventory; // NEW
WeaponInfo weaponInfo;
private Vector3 moveDir;
private Rigidbody2D rb;
private void Awake()
{
rb = GetComponent<Rigidbody2D>();
activeInventory = GetComponent<ActiveInventory>();
}
private void Start()
{
StartCoroutine(AnimCurveSpawnRoutine());
}
private void Update()
{
Vector3 palyerPosition = PlayerController.Instance.transform.position;
if(Vector3.Distance(transform.position, palyerPosition) < pickUpDistance)
{
moveDir = (palyerPosition - transform.position).normalized;
moveSpeed += accelarationRate;
}
else
{
moveDir = Vector3.zero;
moveSpeed = 0f;
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.GetComponent<PlayerController>())
{
DetectPickupType();
Destroy(gameObject);
}
}
private void FixedUpdate()
{
rb.velocity = moveDir * moveSpeed * Time.deltaTime;
}
private IEnumerator AnimCurveSpawnRoutine()
{
Vector2 startPoint = transform.position;
float randomX = transform.position.x + Random.Range(-2f,2f);
float randomY = transform.position.y + Random.Range(-1f,1f);
Vector2 endPoint = new Vector2(randomX,randomY);
float timePassed = 0f;
while (timePassed < popDuration)
{
timePassed += Time.deltaTime;
float linearT = timePassed / popDuration;
float heightT = animCurve.Evaluate(linearT);
float height = Mathf.Lerp(0f,heightY,heightT);
transform.position = Vector2.Lerp(startPoint,endPoint,linearT) + new Vector2(0f,height);
yield return null;
}
}
private void DetectPickupType()
{
switch (pickUpType)
{
case PickUpType.GoldCoin:
//Do GoldCoin Stuff
AudioSource.PlayClipAtPoint(pickupClip,Camera.main.transform.position);
EconomyManager.Instance.UpdateCurrentGold();
//Debug.Log("Gold Coin");
break;
case PickUpType.HealthGlobe:
//Do Health Stuff
AudioSource.PlayClipAtPoint(pickupClip,Camera.main.transform.position);
PlayerHealth.Instance.HealPlayer();
//Debug.Log("Health Gloab");
break;
case PickUpType.StaminaGlobe:
//Do Stamina Globe stuff
AudioSource.PlayClipAtPoint(pickupClip,Camera.main.transform.position);
Stamina.Instance.RefreshStamina();
//Debug.Log("Stmina Golbe");
break;
case PickUpType.Sword: //NEW For inventory
//ActiveInventory.Instance.GetEmptyInventorySlot(); //One or the Other?
activeInventory.GetEmptyInventorySlot();
if (activeInventory != null)
{
InventorySlot.AssingWeaponInfor(weaponInfo.sprite);
}
break;
default:
break;
}
}
}
nothing new was put in the “Active weapon class” But here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ActiveWeapon : Singleton<ActiveWeapon>
{
public MonoBehaviour CurrentActiveWeapon {get; private set;}
private PlayerActions playerActions;
private bool attackButtonDown, isAttacking = false;
private float timeBetweenAttacks;
protected override void Awake()
{
//This is for the Singleton classes
base.Awake();
playerActions = new PlayerActions();
}
private void OnEnable()
{
playerActions.Enable();
}
// Start is called before the first frame update
private void Start()
{
playerActions.Combat.Attack.started += _ => StartAttacking();
playerActions.Combat.Attack.canceled += _ => StoptAttacking();
//don't attack when starting game.
AttackCoolDown();
}
private void StartAttacking()
{
attackButtonDown = true;
}
private void StoptAttacking()
{
attackButtonDown = false;
}
public void NewWeapon(MonoBehaviour newWeapon)
{
CurrentActiveWeapon = newWeapon;
// This is just a safty thing to stop a Coroutine if one is in progress.
AttackCoolDown();
timeBetweenAttacks = (CurrentActiveWeapon as IWeapon).GetWeaponInfo().weaponCooldown;
}
public void WeaponNull()
{
CurrentActiveWeapon = null;
}
private void Update()
{
Attack();
}
private void Attack()
{
if(attackButtonDown && !isAttacking && CurrentActiveWeapon)
{
AttackCoolDown();
(CurrentActiveWeapon as IWeapon).Attack();
}
}
private void AttackCoolDown()
{
isAttacking = true;
StopAllCoroutines();
StartCoroutine(TimeBetweenAttacksRoutine());
}
private IEnumerator TimeBetweenAttacksRoutine()
{
yield return new WaitForSeconds(timeBetweenAttacks);
isAttacking = false;
}
}
The changes are in Pickup, and you were close.
The ActiveInventory isn’t on the same GameObject as the Pickup. We need to get the ActiveInventory on the Player:
private void Awake() {
rb = GetComponent<Rigidbody2D>();
activeInventory = PlayerController.Instance.GetComponent<ActiveInventory>();
}
Again, you’re on the right track, but we need to get the result of GetEmptyInventorySlot() and act on that:
case PickUpType.Sword:
InventorySlot slot = activeInventory.GetEmptyInventorySlot();
if(slot!=null)
{
slot.AssignWeaponInfo(weaponInfo);
}
break;
Please read the guide below for how to format code within your posts. It’ll make it much easier old fogies like me to read the code.
- Forum User Guides : How to apply code formatting within your post
Where should I have the PickUp Script on? I currently have it on the Objects themselves (Coin, Orb Heart and a Sword) because I get a Error It cant find the activeInventory.
case PickUpType.Sword: //NEW For inventory
Debug.Log(activeInventory);
InventorySlot slot = activeInventory.GetEmptyInventorySlot();
if (slot != null)
{
Debug.Log("TEST2");
slot.AssingWeaponInfor(weaponInfo);
Debug.Log("TEST3");
}
break;
default:
The pickup script goes on the item, just like the coin, orb, etc…
One more adjustment to the code in Awake(), I forgot that ActiveWeapon is on a child object of the player.