I am looking to randomly generate 3 items using a loot table. The loot table currently works as expected, it will randomly generate an item based on the item's rarity. However, I don't want there to be duplicates of the same item. How can I set this up? This script is currently attached to 3 separate GameObject item pedestals.
using System.Collections.Generic;
using UnityEngine;
public class ShopItemSpawner : MonoBehaviour
{
[System.Serializable]
public class DropItem
{
public string name;
public GameObject item;
public int dropRarity;
}
public List<DropItem> ShopItemPool = new List<DropItem>();
private void Start()
{
int itemWeight = 0;
for (int i = 0; i < ShopItemPool.Count; i++)
{
itemWeight += ShopItemPool[i].dropRarity;
}
int randomValue = Random.Range(0, itemWeight);
for (int j = 0; j < ShopItemPool.Count; j++)
{
if (randomValue <= ShopItemPool[j].dropRarity)
{
Instantiate(ShopItemPool[j].item, transform.position, Quaternion.identity);
return;
}
randomValue -= ShopItemPool[j].dropRarity;
}
}
}
You could clone the ShopItemPool list and with each item rolled you remove that item from the list. You then need to go all over again by recalculating the total weight.
I find it useful to have a general purpose class for randomizing items with weights, it allows you do this this:
var randomizer = new WeightRandomizer<DropItem>();
foreach (var shopItem in ShopItemPool) {
randomizer.Add(shopItem, shopItem.dropRarity);
}
randomizer.Roll(); // Get a random element based on weight.
randomizer.Take(); // Get a random element based on weight and remove it from the randomizer.
General Purpose Weight Randomizer:
public class WeightRandomizer<T> {
[Serializable]
public class WeightedElement<T> {
public T value;
public int weight;
public WeightedElement (T value, int weight) {
this.value = value;
this.weight = weight;
}
}
private readonly List<WeightedElement<T>> elements = new();
public void Add (T value, int weight) => elements.Add(new WeightedElement<T>(value, weight));
public void AddRange (IEnumerable<WeightedElement<T>> weightedElements) => elements.AddRange(weightedElements);
public int TotalWeight() => elements.Sum(x => x.weight);
public T Roll() => Pick(false);
public T Take() => Pick(true);
private T Pick (bool remove) {
if (elements.Count == 0) {
Debug.LogWarning($"{nameof(WeightRandomizer<T>)} is missing elements.");
return default(T);
}
var roll = Random.Range(0, TotalWeight());
var selectedIndex = elements.Count - 1;
var selected = elements[selectedIndex].value;
for (var i = 0; i < elements.Count; i++) {
var element = elements[i];
// Found an element with a low enough value.
if (roll < element.weight) {
selected = element.value;
selectedIndex = i;
break;
}
// Keep searching for an element with a lower value.
roll -= element.weight;
}
// Sometimes we want to take and remove the element from the pool.
if (remove) elements.RemoveAt(selectedIndex);
return selected;
}
}
I am new to C# and I do not understand this example with Priority Queue
First comes struct
public struct pqItem
{
public int priority;
public string name;
}
then modified class
public class PQueue : Queue
{
public PQueue : base() {}
public override object Dequeue()
{
object [] items;
int x, min, minindex;
items = this.ToArray();
min = (pqItem)items[0].priority;
for(int x = 1; x <= items.GetUpperbound(0); x++)
if ((pqItem)items[x].Priority < min)
{
min = (pqItem)items[x].Priority;
minindex = x;
}
I do not understand how struct indexing works here.
min = (pqItem)items[0].priority;
Why not (pqItem).items[0]?The code is from McMillan's boook Data structures and algorithms.
So I got this base class
abstract class Item
{
private int x, y, ataque, defesa, saude, raridade;
private char appearance;
private bool pickedUp;
private readonly Random rng = new Random();
public Item(Map argMap, int argAtaque, int argDefesa, int argSaude, int argRaridade, char argAppearance)
{
bool empty = false;
while (!empty)
{
x = rng.Next(1, argMap.ReLengthX() - 1);
y = rng.Next(1, argMap.ReLengthY() - 1);
if (!argMap.CheckTile(y, x)) empty = true;
}
pickedUp = false;
ataque = argAtaque;
defesa = argDefesa;
saude = argSaude;
raridade = argRaridade;
appearance = argAppearance;
}
}
And I got this derived class
class Armadura : Item
{
public Armadura(Map argMap, int ataque, int defesa, int saude, int raridade, char appearance) : base(argMap, ataque, defesa, saude, raridade, appearance)
{
ataque = -1;
defesa = 2;
saude = 0;
raridade = ReRNG().Next(Convert.ToInt32(Math.Round(argMap.ReLengthY() * 0.02)), Convert.ToInt32(Math.Round(argMap.ReLengthY() * 0.04)));
appearance = ' ';
}
}
So my question is, how do I get to set the values on :base, using the values that I set on the derived constructor (for example, set the base argAtaque with ataquewwww, therefore, argAtaque being equal to '-1')?
I tried this using a few ways but I didn't get this to work in any way.
I thank you in advance!
The : base() syntax will work for constants and parameters, but not for more complex expressions with side-effects (as you found).
You'll be needing a initialization method on the base class.
abstract class Item
{
...
// If you use this constructor, call Setup() afterwards.
public Item() {}
// Constructor including a call to Setup()
public Item(Map argMap, int argAtaque, int argDefesa, int argSaude, int argRaridade, char argAppearance)
{
Setup(argMap, argAtaque, argDefesa, argSaude, argRaridade, argAppearance);
}
// Sets private variables for this Item
protected void Setup(Map argMap, int argAtaque, int argDefesa, int argSaude, int argRaridade, char argAppearance)
{
bool empty = false;
while (!empty)
{
x = rng.Next(1, argMap.ReLengthX() - 1);
y = rng.Next(1, argMap.ReLengthY() - 1);
if (!argMap.CheckTile(y, x)) empty = true;
}
pickedUp = false;
ataque = argAtaque;
defesa = argDefesa;
saude = argSaude;
raridade = argRaridade;
appearance = argAppearance;
}
}
Now you can setup the base class with the calculated values:
class Armadura : Item
{
public Armadura(Map argMap)
{
int ataque = -1;
int defesa = 2;
int saude = 0;
int raridade = ReRNG().Next(Convert.ToInt32(Math.Round(argMap.ReLengthY() * 0.02)), Convert.ToInt32(Math.Round(argMap.ReLengthY() * 0.04)));
char appearance = ' ';
Setup(argMap, ataque, defesa, saude, raridade, appearance);
}
All you have to do is set ataque in the child constructor, as it will override what ataque is being set to in your base class. The base constructor is called first, then the child constructor.
For this to work, you will need to make your private variables protected in the base class. This will make them private in the child class.
This is my first question. Please correct me if I have some mistake. THX
//Form.cs
Sweet sweet = new Sweet();
Donut donut = new Donut();
//classify the condition with int i;
if (i==0)
score.Text=sweet.TambahPoin().ToString();
else if (i==1)
score.Text=donut.DoublePoin().ToString();
This is the parent class
class Sweet
{
//field
int m_poin;
//properties
public int Poin
{
get{return m_poin;}
set
{
if (value < 0)
m_poin = 0;
else
m_poin = value;
}
}
//Method
public int TambahPoin()
{
Poin += 10;
return Poin;
}
}
I want to make a method that will double the TambahPoin() with condition it will add the score before.
if the initial Poin = 100 and then call TambahPoin() method will become 110 after that i want to call DoublePoin method so the score become 110+20=130.(I have tried with my own solution before but the score becomes 20 not 130)
You probably want something like this:
public int TambahPoin()
{
m_poin += 10;
return m_poin;
}
public int DoublePoin()
{
for (int i = 0; i < 2; i++)
{
TambahPoin();
}
return m_poin;
}
public int IncreasePoin()
{
if (m_poin == 100) TambahPoin();
DoublePoin();
return m_poin;
}
Inside the class you can work with private fields and is quicker because you are not calling get/set methods from properties.
I have a generic type called Vector<T>, I created it as so, cause the T might be float or Complex :
public class Vector<T>
{
#region Properties
public ulong Length
{
get
{
return _length;
}
}
public VectorType VectorType
{
get
{
return _vectorType;
}
}
#endregion
#region Indexers
public T this[ulong index]
{
get
{
return _data[index];
}
set
{
_data[index] = value;
}
}
#endregion
#region Constructors
public Vector(VectorType vectorType, T[] data)
{
if (!((data is float[]) || (data is Complex[])))
{
throw new InvalidDataException("Data must be array of float or array of Complex");
}
_data = new T[_length = (ulong)data.Length];
for (ulong i = 0; i < _length; i++)
{
_data[i] = data[i];
}
_vectorType = vectorType;
}
public Vector(VectorType vectorType, Vector<T> vector)
{
_data = new T[_length = vector.Length];
for (ulong i = 0; i < _length; i++)
{
_data[i] = vector[i];
}
_vectorType = vectorType;
}
#endregion
#region Methods
//Unity Matrix, this vector has 1/N everywhere
public static Vector<float> e(VectorType vectorType, ulong length)
{
var data = new float[length];
for (ulong i = 0; i < length; i++)
{
data[i] = (float)1 / length;
}
var vectorE = new Vector<float>(vectorType, data);
return vectorE;
}
public float Sum()
{
float sum = 0;
if (_data is float[])
{
sum = (_data as float[]).Sum();
}
else
{
if (_data is Complex[])
{
for (ulong i = 0; i < _length; i++)
{
sum += (float)
Math.Sqrt(Math.Pow((_data[i] as Complex?).Value.Real, 2) +
Math.Pow((_data[i] as Complex?).Value.Imaginary, 2));
}
}
}
return sum;
}
public bool CheckIfSochasitc()
{
return Math.Abs(Sum() - 1) < float.Epsilon;
}
public void Normalize()
{
var sum = Sum();
if (_data is float[])
{
for (ulong i = 0; i < _length; i++)
{
float x = ((float) _data[i])/sum;
_data[i] = (T)x;
}
}
}
#endregion
#region Operators
//I omitted the code inhere to avoid overload
#endregion
#region Fields
private ulong _length;
private readonly VectorType _vectorType;
private T[] _data;
#endregion
}
public enum VectorType
{
Row,Column
}
My problem is that I have a generic array (if I can call it so) :
private T[] _data;
And I have the Normalize() method:
public void Normalize()
{
var sum = Sum();
if (_data is float[])
{
for (ulong i = 0; i < _length; i++)
{
//Here is the problem
_data[i] = ((_data[i] as float?) / sum);
}
}
}
This doesn't work saying can't cast float to T tried to search but couldn't find helpful aide, any clarification I'd be thankful.
Update :
The Sum() method always returns a float
It's not clear why you're converting to float? at all (or why you're using ulong as the index variable type...) but you just need to cast the result back to T - otherwise you can't assign it back into an array of type T[]. Additionally, you need to cast to object (in order to convert back to T:
float x = ((float) (object) data[i]) / sum;
data[i] = (T) (object) x;
You can use float? for the first line, with as, to avoid boxing - but then you need to get the non-nullable value:
float x = (data[i] as float?).Value / sum;
Both are pretty ugly :(
As noted in comments though, this sort of thing is usually an indication of the design not really being properly generic at all. We don't know what type Sum() returns, but you should consider just how "general" your type is to start with.
May be you can try this
if (typeof(_data) == float[])
{
for (ulong i = 0; i < _length; i++)
{
_data[i] = ((_data[i] as float?) / sum);
}
}