(C#) Instantiated Under Conditions, Else Reuse - c#

I'm extremely new to programming so bear with me.
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
GameObject newItem = Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
newItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
newItem.GetComponent<ButtonSetter>().item_count += 1;
newItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
//SEE CONTINUED POST
}
}
So, within the else statement, I want to basically say,
They got their first wood, so we created a panel to display information about the wood they have. Now I want to say, since we have already instantiated the displayPanel, we just want to work from that and adjust the object's variable that controls the integer part of "Wood: 0"
This should be
newItem.GetComponent<ButtonSetter>().item_count
But if I try accessing that within the else statement as shown:
newItem.GetComponent<ButtonSetter>().item_count += 1;
It tells me that newItem does not exist within the current context.
How can I Instantiate something under certain conditions, then after the condition was met, take the instantiated object and work with its script/variables?
Visual representation of my issue

I think you need to restructure your code a little bit.
So here's how I would do it
public class Item : MonoBehaviour {
public string itemName;
public int count;
}
I will create a base class Item for wood, iron or whatever is there in your game.
Next, I will Create a Handler Script, that keeps track of all Items that are clicked/not Clicked
Handler.cs
public class Handler : MonoBehaviour {
public Item itemPrefab;
public Dictionary<string, Item> displayItems= new Dictionary<string, Item>();
public void OnWoodClicked()
{
OnItemClicked("wood");
}
public void OnIronClicked()
{
OnItemClicked("iron");
}
private void OnItemClicked(string itemKey)
{
if (displayItems.ContainsKey(itemKey))
{
displayItems[itemKey].count++;
}
else
{
Item item = Instantiate(itemPrefab);
item.count = 1;
item.itemName=itemKey;
displayItems.Add(itemKey, item);
}
}
}
So To keep track of created items, I have created a dictionary public Dictionary<string, Item> displayItems;
In this script OnItemClicked(string itemKey) Method will check If Item of this type is already created or not. (By checking if that key exists)
If that item is not created then we will Instantiate new Item (Prefab of display Item you want to show) and add that to the dictionary according to its key.
But if it already exists, then simply access that object as displayItems[itemKey] as you like
So for example, if you click on Wood, you will get access to wood display item as displayItems["wood"].
I hope this helps.
Just place a comment if you want me to make it clearer.

As it's currently written, newItem only lives in the "IF" clause of the code, so the "ELSE" clause can't see it.
If you assign it to a class-level field (like it appears you have _hasWood assigned at the class level) then you will be able to access it in either the "IF" or "ELSE" blocks, and maintain the count for the life of the object "clickWood()" is called from.
private bool _hasWood;
private GameObject _woodItem;
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
_woodItem= Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
_woodItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
_woodItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
}
}

Related

How to build a custom loop activity in Elsa workflows

I'm trying to make a custom activity that will eventually do a complicated database query or API call to get a bunch of records and loop over them. I'm sure it could be done with the built in flow control activities, but I want to make this usable by non-programmers who don't know or care what a foreach loop is, so putting a lot of functionality into one box is good.
My first attempt was to inherit from ForEach and do some initialization before letting OnExecute do its thing, but the result feels somewhat hacky.
public class FancyForEach : ForEach
{
private bool? Initialized
{
get
{
return GetState<bool?>("Initialized");
}
set
{
SetState(value, "Initialized");
}
}
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
if (Initialized != true)
{
Items = GetThingsFromDatabase();
Initialized = true;
}
return base.OnExecute(context);
}
protected List<DatabaseThings> GetThingsFromDatabase()
{
// Fancy stuff here, including paging eventually.
}
}
It seems like it would be a little cleaner to instantiate a ForEach somewhere within the activity rather than inherit from it, but I can't puzzle out a way to make that work. I imagine a decent solution would be to trigger another workflow for each record, but I'd rather not do that, again to make this easy to digest for people who aren't programmers.
Can anyone offer a suggestion on the best way to make this work? This is my first project using Elsa, so maybe I'm approaching it from an entirely wrong direction!
If I understand correctly, your activity is responsible for loading in the data and looping over it, while the user of the activity should be able to specify what happens in each iteration.
If so, then you might implement something like this:
[Activity(
Category = "Control Flow",
Description = "Iterate over a collection.",
Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done }
)]
public class FancyForEach : Activity
{
private bool? Initialized
{
get => GetState<bool?>();
set => SetState(value);
}
private IList<DatabaseThings>? Items
{
get => GetState<IList<DatabaseThings>?>();
set => SetState(value);
}
private int? CurrentIndex
{
get => GetState<int?>();
set => SetState(value);
}
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
if (Initialized != true)
{
Items = GetThingsFromDatabase();
Initialized = true;
}
var collection = Items.ToList();
var currentIndex = CurrentIndex ?? 0;
if (currentIndex < collection.Count)
{
var currentValue = collection[currentIndex];
var scope = context.CreateScope();
scope.Variables.Set("CurrentIndex", currentIndex);
scope.Variables.Set("CurrentValue", currentValue);
CurrentIndex = currentIndex + 1;
context.JournalData.Add("Current Index", currentIndex);
// For each iteration, return an outcome to which the user can connect activities to.
return Outcome(OutcomeNames.Iterate, currentValue);
}
CurrentIndex = null;
return Done();
}
protected List<DatabaseThings> GetThingsFromDatabase()
{
// Fancy stuff here, including paging eventually.
}
}
This example loads the database items into memory once and then stores this list in workflow state (via Items) - which may or may not be desirable, since this has the potential of increasing the size of the workflow instance significantly depending on the size of each record and number of records.
A more scalable approach would be to load just one item per iteration, keeping track of the current index that was loaded, incrementing it (i.e. pagination with a page size of 1).

How to implement next button and previous button from a list of models in unity?

I have list of 10 models in a list. Initially I have a model present in my scene say model[0].When I click next button it must show model[1] till model[9].Similarly the previous button in the reverse order.
I have written a logic inside the code.I know it is not a standard one but will do the job I believe.Apart from this logic any simple way to implement.
public GameObject [] dress;
public void PreviousModel()
{
int counter = dress.Length;//Dont know what to write here
Debug.Log(counter);
if(counter > -1)
{
counter--;
dress[counter].SetActive(true);
dress[counter+1].SetActive(false);
}
}
public void NextModel()
{
}
that should do the trick
public GameObject [] dress;
private int _index;
public void PreviousModel()
{
_index = Mathf.Clamp(_index-1,0,9);
// code to show your model dress[_index] ...
}
public void NextModel()
{
_index = Mathf.Clamp(_index+1,0,9);
// code to show your model dress[_index] ...
}
Alternatively to the answer using Clamp you could also wrap around the index like
public GameObject [] dress;
private int _index;
public void PreviousModel()
{
// Hide current model
dress[index].SetActive(false);
_index--;
if(index < 0)
{
index = dress.Length -1;
}
// Show previous model
dress[index].SetActive(true);
}
public void NextModel()
{
// Hide current model
dress[index].SetActive(false);
_index++;
if(index > dress.Length -1)
{
index = 0;
}
// Show next model
dress[index].SetActive(true);
}
So that of you click next on the last entry it jumps to the first instead of doing nothing.
if I understand your comment
the index value I have togive the same as the model that is present in scene
correctly you mean that at start of this script you need to get the current index dependend on the currently active model:
private void Start()
{
// Get the index of the currently active object in the scene
// Note: This only gets the first active object
// so only one should be active at start
// if none is found than 0 is used
for(int i = 0; i < dress.Length; i++)
{
if(!dress[i].activeInHierachy) continue;
_index = i;
}
// Or use LinQ to do the same
_index = dress.FirstOrDefault(d => d.activeInHierarchy);
}

Having trouble adding to a List that exists

So I have a user script that contains a List inventory. Then I use a public static User user to access a single user's information throughout the game.
The problem is at some point I'm losing the link to that inventory.
In the awake function if I call Debug.Log(User.user.inventory.Count), it prints 0.
however if i try and access this inventory at any future time I get a Null Reference error as if it doesn't exist and can't add to it.
If I make the size of the list in the inspector to 1, then it somehow exists and I can add things to it forever, however then I have a dead spot at index 0.
private void AddItemToInventory(ItemObject item, Image uiSprite)
{
User.user.inventory.Add(item);
uiSprite.sprite = GenerateOrbRaritySprite(RaritySprites.raritySprites, item.rarity);
uiSprite.enabled = true;
}
It is failing on the inventory lookup, even though I can see it in the inspector fine.
My User has the following Awake() function so that it stays between scenes:
void Awake()
{
if(user == null)
{
user = this;
DontDestroyOnLoad(gameObject);
}
else if (user != this)
{
Destroy(gameObject);
}
}
Any ideas? Thank you!
Try with just one User.
private void AddItemToInventory(ItemObject item, Image uiSprite)
{
user.inventory.add(item);
uiSprite.sprite = GenerateOrbRaritySprite(RaritySprites.raritySprites, item.rarity);
uiSprite.enabled = true;
}
And since this method doesn't have access to the user the user will need passed in as a parameter.
private void AddItemToInventory(User user, ItemObject item, Image uiSprite)
{
user.inventory.add(item);
uiSprite.sprite = GenerateOrbRaritySprite(RaritySprites.raritySprites, item.rarity);
uiSprite.enabled = true;
}

How to get field of new instance from one class to another

So I am implementing a rudimentary Hotel booking system with a check in and check out method. For the check in and check out selections, I have a switch statement that allows the user to pick a room by selecting a number. I have a truncated version of the code below, as the case statement pretty much are similar in nature:
case 1:
if (true)//this is supposed to check if the room is booked
{
Console.WriteLine("This room is already booked, please try another place");
}
else
{
var instance1 = new BookingMethods();
instance1.bookRoom1();
revenueGenerated += 100;
}
break;
For the if statements, I'm trying to check if the rooms are booked or not, and I have created objects in another class along the methods for checking in and out. Below is how I did this:
class BookingMethods
{
Room room1 = new Room();
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}
I want the if statement to be able to read room1.occupied for the condition, but I can't seem to be able to reference it correctly. I have bookRoom1() in my BookingMethods.cs from the case statement, which is in my Program.cs. I figured I don't have to move my Room object from BookingMethods.cs to Program.cs to do this, and I haven't been able to figure out a way to call it.
You did not specify an access modifier for room1. If you do not provide one, it is set to private by default, meaning it is only accessible in the class in which it is declared. In addition to this, you should really create a property.
This should get you going:
var instance1 = new BookingMethods();
switch (instance1.room1.occupied)
{
case true:
Console.WriteLine("This room is already booked, please try another place");
break;
default:
instance1.bookRoom1();
revenueGenerated += 100;
break;
}
class BookingMethods
{
public Room room1 {get; set;};
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}
Make room1 a field
class BookingMethods
{
Room room1 {get; set;};
public void bookRoom1()
{
//code for generating ints
room1.occupied = true;
room1.numGuests = guests1;
room1.daysBooked = staying1;
room1.roomType = "luxury";
}
}

How can I check if the clicked object is the topmost in C# XNA?

I'm trying to reproduce the simple window interface objects in C# XNA like labels, listboxes, textboxes and panels. All objects consequentially derive from basic abstract class (XNAInterfaceObject) that draws an object and updates it. In update method it interacts with a mouse and keyboard and raises various events.
The problem is when two interface objects are one over another (e.g. popup context menu over listbox) and both have non-null events - the code fires both events, when I just need the event of the topmost object. How can I check which object is the topmost? Or make somehow the top object overlap the lower. I thought about global variable which would keep the reference for the last clicked object, and other objects would check if this variable is null to proceed with their events, but I think it is a rough solution and there exists far more elegant one.
Sorry for my language, I'm not a native English-speaker.
I would probably break this issue down into two components:
Determining the order of interface objects.
Only triggering the events on the top-most object when there's an overlap.
Addressing part one is simple. Include a 'layer' field/property in the base class that specifies the depth of the object. In most game node classes I include this regardless, as it's useful in drawing. You may want a separate layering system for interface ordering if things get a bit more complex, and the downside to this approach is that you can get overlaps in which the layers are the same.
As #Attila has suggested, you can otherwise manage a Z-Ordered list of interface elements. In this case ordering is managed by index, and it's easy to manage but you can't also use this information for drawing without some additional processing and it won't be as quick as a simple value comparison.
Property
public class InterfaceComponent
{
// Class members...
private float layer;
public float Layer { get { return layer; } set { layer = Math.Abs(value); } }
public bool InFrontOf(InterfaceComponent other) { return this.Layer < other.Layer; }
}
Z-Ordered List
public class InterfaceComponent
{
private static List<InterfaceComponent> zOrder = new List<InterfaceComponent>();
// Class Members....
public InterfaceComponent()
{
// Construct class...
zOrder.Add(this);
}
private void SetZOrder(int order)
{
if (order < 0 || order >= zOrder.Count)
return;
zOrder.Remove(this);
zOrder.Insert(order, this);
// There are more efficient ways, but you get the idea.
}
public void SendBack() { SetZOrder(zOrder.indexOf(this) + 1); }
public void SendToFront() { SetZOrder(0); }
// etc...
}
Part Two
There are multiple ways to approach part two. The most obvious is to run a check against all interface components for intersection and layer property, or in the case of a Z-Ordered list, all components higher up the list (approaching 0 in my example) for intersection.
This can end up being pretty expensive, even if you use screens to make the list smaller. Instead you can manage a list of raised events and process them after you handle input. For example...
public static class InterfaceEvents
{
public static List<EventData> List = new List<EventData>();
public static void Resolve()
{
while (List.Count > 0)
{
for (int i = List.Count - 1; i > 0; i--)
{
if (List[i].EventType == List[0].EventType && List[i].Sender.Intersects(List[0].Sender))
{
if (List[i].Sender.Layer < List[0].Layer) // However you choose to manage it.
{
List[0] = List[i];
List.RemoveAt(i);
}
else
List.RemoveAt(i);
}
}
// Toggle event from List[0]
List.RemoveAt(0);
}
}
}
public struct EventData
{
public InterfaceComponent Sender;
public int EventType;
}
Anyway, those are my thoughts. It's pretty late at night, so I hope everything's remained sensible and there are no glaring mistakes.
Usually in GUI there is a list of visibility ordering (z-order) that maintains what is on top of what. Using this technique (assigning a z order to each of your component) you can check if there is anything more toward the top of a clicked component that also includes the clicked coordinates -- if there is, do not handle the click (som other component is on top, that will handle it); otherwise this component is the topmost one to handle the click
A simple solution is creating a list in your Game class:
List<XNAInterfaceObject> controls;
You can then use the order of the list for your problem. Think of the first element in your list as the control that is at the front. In the GetControlAt() method of your game, you can loop through the controls from front to back:
protected override void Update(GameTime gameTime)
{
...
MouseState ms = Mouse.GetState();
if (ms.LeftButton == ButtonState.Pressed)
{
XNAInterfaceObject control = GetControlAt(ms.X, ms.Y);
if (control != null)
control.MouseClickMethod(ms);
}
...
}
private XNAInterfaceObject GetControlAt(int x, int y)
{
for (int i = 0; i < controls.Count; i++)
{
if (controls[i].Rectangle.Contains(x, y)
{
return controls[i];
}
}
return null;
}
This means that a XNAInterfaceObject should have a Rectangle property and a MouseClickMethod(). Keep in mind that when drawing your controls, you have to loop through the list backwards.

Categories

Resources