So prototyped a mechanic idea last night and it “works”. Well, it actually works quite well but the code is horrendous and I’m trying to determine if I’m doing things correctly.
You can test the prototype here: https://theoddpotion.itch.io/project-trade-v0
Basically I have a “Hand” that is controlled laterally with the Horizontal and Vertical axes. The Hand can Grab() and Drop() an Item from a Slot.The “Item” just has a few properties like name, cost, stackable, count, etc. And I have a “Slot” which holds a reference to an Item that it is currently holding. You can AddItem(), RemoveItem(), GetItem() and check if HasItem().
All of these Scripts are MonoBehaviours and attached to relative prefabs. I created a grid of Slots for a makeshift “inventory” and with this setup I am able to move the Hand around, hold a button down to grab an Item from the Slot below the Hand and release the button to drop the Item into the Slot below the Hand. And with some hacking I’ve managed to implement a basic stacking setup where if you Grab() a stackable Item you pick up the whole stack, and you can press another button while grabbing a stack of Items to Drop() one Item.
The whole thing works but right now there is too much inter-dependency and I’m not exactly sure how to solve it. Maybe using coroutines for Grab() and Drop()? I’m not really sure.
Note: I haven’t implemented an Item database yet, so Item is a MonoBehaviour. But I intend to use ScriptableObjects to make that easier than just having a separate prefab for each Item.
using UnityEngine;
using System.Collections;
public class Item : MonoBehaviour {
public string itemName = "Default Item";
public int cost;
public bool stackable;
public int count = 1;
public Slot lastSlot;
}
using UnityEngine;
using System.Collections;
public class Slot : MonoBehaviour {
public Item heldItem;
//We are assuming here that: the item passed in
// 1) Either we don't have a heldItem -or-
//the item passed in is the same type of item as the heldItem
public void AddItem(Item item){
//if we aren't holding an item
if (heldItem == null) {
//set our reference to passed in item
heldItem = item;
//set position of new held item to ItemPoint
//which is just an empty GameObject attached to the Slot object.
heldItem.transform.position = transform.FindChild ("ItemPoint").transform.position;
} else { //if we are holding an item
//destroy the passed in item, basically deleting the item from the Hand
//because we already have an item in the slot to represent the items in the slot
Destroy (item.gameObject);
//and increase the count of the heldItem
heldItem.count += item.count;
}
}
//When we Grab an Item from a Slot we RemoveItem
//which just sets a reference to this Slot inside the heldItem and then cuts the tie
public void RemoveItem(){
heldItem.lastSlot = this;
heldItem = null;
}
public Item GetItem(){
return heldItem;
}
public bool HasItem(){
if (heldItem) {
return true;
}
return false;
}
public void IncreaseItemCount(int amount){
heldItem.count += amount;
}
}
using UnityEngine;
using System.Collections;
public class Hand : MonoBehaviour {
public float moveSpeed = 5f;
public Item grabbedItem;
public bool isGrabbingItem;
public int grabbedCount = 0;
public Slot slotBelow;
public LayerMask slotLayerMask;
//item prefabs. Just a gameobject with an Item component
public Item redItemPrefab;
public Item blueItemPrefab;
private Vector3 hitPoint = Vector3.zero;
void Update(){
//BASIC MOVING
Vector3 moveDir = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical"));
moveDir.Normalize ();
Move (moveDir);
//
//if null we are not over a slot
slotBelow = GetSlotBelow ();
//allows to press and hold button to grab item and release to drop
if (Input.GetButtonDown ("Fire6")) {
Grab ();
}
if (Input.GetButtonUp ("Fire6")) {
Drop ();
}
if (Input.GetButtonDown ("Fire3")) {
DropOne ();
}
//testing stuff
if (Input.GetButtonDown ("Fire2")) {
AddItemBelow ();
}
if (Input.GetButtonDown ("Fire1")) {
if (isGrabbingItem) {
DestroyItemInHand ();
}
}
//set grabbed objects position if we have one
if (grabbedItem && isGrabbingItem) {
grabbedItem.transform.position = transform.FindChild("ItemPoint").transform.position;
}
}
void LateUpdate(){
if (slotBelow) {
//A weird snapping experiment. Idk.
//Vector3 nextPos = slotBelow.transform.position + Vector3.up * 1f;
//transform.position = Vector3.Lerp (transform.position, nextPos, 2.5f * Time.deltaTime);
}
}
void Move(Vector3 dir){
transform.position += dir * moveSpeed * Time.deltaTime;
}
void Grab(){
//If we aren't grabbing an item
if (isGrabbingItem == false) {
//and there is a slot below with an item in it
if (slotBelow != null && slotBelow.HasItem()) {
//get reference to item in slot
Item item = slotBelow.GetItem ();
//remove item from slo
slotBelow.RemoveItem ();
//we are grabbing the referenced item and store the number of items.
isGrabbingItem = true;
grabbedItem = item;
grabbedCount = grabbedItem.count;
//Test Debug
if (grabbedItem.count > 1) {
Debug.Log ("Item Name: " + grabbedItem.itemName + " x" + grabbedItem.count + " --- Item Cost: " + grabbedItem.cost);
} else {
Debug.Log ("Item Name: " + grabbedItem.itemName + " --- Item Cost: " + grabbedItem.cost);
}
}
}
}
void Drop(){
//if we are grabbing an item
if (isGrabbingItem) {
//and there is a slot below us
if (slotBelow != null) {
//....and it has an item...
if(slotBelow.HasItem()){
//.....and it is the same type of item as the grabbedItem.. whew.
if (grabbedItem.itemName == slotBelow.GetItem().itemName && slotBelow.GetItem().stackable) {
//add the item to the slot below
slotBelow.AddItem (grabbedItem);
//and clear our grabbing stuff
ClearGrabbed ();
} else { //if it is not the same type of item as the grabbedItem.
//get a reference to the slot the grabbedItem was in last and add it to the slot
Slot slot = grabbedItem.lastSlot;
slot.AddItem (grabbedItem);
//and clear our grabbing stuff
ClearGrabbed ();
}
}else{ //if the slot below doesn't have an item
//add the grabbed item to the slot
slotBelow.AddItem (grabbedItem);
//and clear our grabbing stuff
ClearGrabbed ();
}
} else { //and if there is not a slot below us
//get a reference to the slot the grabbedItem was in last and add it to the slot
Slot slot = grabbedItem.lastSlot;
slot.AddItem (grabbedItem);
//and clear our grabbing stuff
ClearGrabbed ();
}
}
}
void DropOne(){
//if we are grabbing and item and it's stackable
if (isGrabbingItem && grabbedItem.stackable) {
//and we have a slot below us
if (slotBelow != null) {
//...and it has an item..
if(slotBelow.HasItem()){
//...and its the same type of item as the grabbed item...
if (grabbedItem.itemName == slotBelow.GetItem().itemName) {
//...and we have more than 1 item in our Hand
if (grabbedItem.count > 1) {
//decrease the amount of grabbed items by 1
grabbedItem.count--;
//and increase the amoutn of items in the slot by 1
slotBelow.IncreaseItemCount (1);
} else { //if this is the last item in our hand
//destroy the object in hand
Destroy (grabbedItem.gameObject);
ClearGrabbed ();
}
}
}else{ //if the slot below has no item
//create an item and set its count to 1
Item item = Instantiate (grabbedItem) as Item;
item.count = 1;
//and add the item to the slot
slotBelow.AddItem (item);
//and decrease the amount of items we are grabbing by 1
grabbedItem.count--;
//check if that was the last item
if (grabbedItem.count <= 0) {
//destroy item in hand
Destroy (grabbedItem.gameObject);
ClearGrabbed ();
}
}
}
}
}
void DestroyItemInHand(){
ClearGrabbed ();
Destroy (grabbedItem.gameObject);
}
//shoot raycast down, if hits a Slot
Slot GetSlotBelow(){
RaycastHit hit;
if (Physics.Raycast (transform.position, Vector3.down, out hit, 2f, slotLayerMask)) {
hitPoint = hit.point;
return hit.transform.GetComponent<Slot> ();
}
Debug.DrawLine (transform.position, transform.position + Vector3.down * 2f, Color.red);
return null;
}
//draws a sphere on the Slot below the hand
void OnDrawGizmos(){
Gizmos.color = Color.red;
Gizmos.DrawSphere (hitPoint, 0.05f);
}
//testing
void AddItemBelow(){
if (slotBelow) {
if (slotBelow.HasItem () == false) {
if (Random.Range (0, 100) > 50) {
Item item = Instantiate (redItemPrefab) as Item;
slotBelow.AddItem (item);
} else {
Item item = Instantiate (blueItemPrefab) as Item;
slotBelow.AddItem (item);
}
}
}
}
void ClearGrabbed(){
isGrabbingItem = false;
grabbedItem = null;
grabbedCount = 0;
}
}
All you should have to do if you were to want to quickly test this is create a Cube object named Slot. Scale it on y to 0.05 or something small and create an empty child object called ItemPoint and set its y position to 5(because of the scaling it will actually be 0.5 units above the Slot). Attach the Slot script to the Slot object and make sure to create a new layer named Slots and assign the slot to that layer. Then create a grid of these Slots in the scene view.
Then create another Cube object named RedItem, set its scale to 0.25 on all axes. Then attach the Item script to it. Set its name to RedItem, cost to whatever, stackable to true and 2 for count. Leave lastSlot blank. Do the same thing again for a BlueItem but scale it just a bit bigger and set stackable to false.
Then create another Cube object named Hand. Set it’s scale to (0.5, 0.1, 0.5) and create an empty child object and set it’s position somewhere just below the hand and name it ItemPoint. Attach the Hand script to it and set the RedItemPrefab and BlueItemPrefab to the item prefabs that were just made and make sure to set the slotLayerMask to the Slots layer. And that should be it. Just make sure to have a Hand and some Slots in your Hierarchy and thats it. You may also need to change the input buttons. During play just press whatever button to AddItemBelow() to get some items in there to play with.
I just don’t know how to make it better… I know it’s a lot but any help would be greatly appreciated. =)
