Sun Script RenderProbe on time of day - c#

I'm tring to make a unity sun script that renders the light probe at certain time of the day taken from an array.
The day is from 0-1 interval ( 0 night, 0.25 sunrise, 0.5 midday, 0.75 sunset, 1 night)
To be honest even if I check the statement currentTimeOfDay == 0.75 when the value is reached the print does not occur
And how do I check the statement for multiple array?
public float secondsInFullDay = 120f;
[Range(0,1)]
public float currentTimeOfDay = 0f;
private float[] floatDay = new float[4] {0f, 0.25f, 0.5f, 0.75f};
public float timeMultiplier = 1f;
void Update() {
currentTimeOfDay += (Time.deltaTime / secondsInFullDay) * timeMultiplier;
if (currentTimeOfDay >= 1) {
currentTimeOfDay = 0;
}
if(currentTimeOfDay == floatDay[0]){
reflectionProbe.RenderProbe();
print ("refresh probe");
}
}
This only prints at 0 value
foreach (float x in floatDay){
if (x.Equals (currentTimeOfDay)){
print ("refresh probe");
}
}
L.E
I managed to check the statement against array but it prints multiple times that means that will cause unnecessary load
If the time timeMultiplier is set to 10 instead of 1 then the print is one time when reached the value.
Is there a way to multiply the array with a float and get new array?
private float[] floatDay = new float[4] {0, 250, 500, 750};
TimeOfDay = currentTimeOfDay * 1000 * timeMultiplier;
TimeOfDay = Mathf.Round(TimeOfDay);
foreach (float x in floatDay){
if (TimeOfDay == x){
reflectionProbe.RenderProbe();
print ("refresh probe");
}
}
L.E 2
fixed it , but doesn't seem that compact but it works
private float[] floatDay = new float[4] {0, 2500, 5000, 7500};
TimeOfDay = currentTimeOfDay * 10000 / timeMultiplier;
TimeOfDay = Mathf.Round(TimeOfDay);
foreach (float x in floatDay){
float y = x / timeMultiplier;
if (TimeOfDay == y){
reflectionProbe.RenderProbe();
print ("refresh probe");
}
}

This only prints at 0 value
foreach (float x in floatDay){
if (x.Equals (currentTimeOfDay)){
print ("refresh probe");
}
}
because floating points are never accurate. Your value in never exactly equal to the value you compare it to.
Use this instead:
foreach (float x in floatDay){
if (x > currentTimeOfDay - 1e-7f && x < currentTimeOfDay + 1e-7f){
print ("refresh probe");
}
}
if it still doesn't work, change 1e-7f to 1e-6f, 1e-5f, etc...

Related

Mouse movement based on head rotation

I've a device which gives a quaternion data about the direction the device is facing and I want to use this data to move the mouse on-screen.
I've written the following code until now, but even when the device is idle (and I'm not getting major change in angle), I'm noticing mouse movement towards top-left
Setting the next position:
public void OnDataReceived(Quaternion quat)
{
var angle = GetAngle(quat.X, quat.Y, quat.Z, quat.W);
angle.Z = 0f;
var diff = angle - lastAngle;
lastAngle = angle;
var dtX = (int)(Math.Tan(diff.Y) * MoveMultiplier);
var dtY = (int)(Math.Sin(diff.X) * MoveMultiplier);
User32Wrapper.GetCursorPos(ref current);
next.x = Math.Clamp(current.x + dtX, 0, Width);
next.y = Math.Clamp(current.y + dtY, 0, Height);
isDirty = true;
}
Moving mouse (which is being called continuously):
private void MoveMouse(float deltaTime)
{
if (isDirty)
{
var dt = Speed * deltaTime;
var x = (int)Lerp(current.x, next.x, dt);
var y = (int)Lerp(current.y, next.y, dt);
if (x >= 0 && x < Width && y >= 0 && y < Height)
{
current.x = x;
current.y = y;
User32Wrapper.Move(x, y);
if (Math.Abs(current.x - next.x) < precision && Math.Abs(current.y - next.y) < precision)
{
isDirty = false;
User32Wrapper.Move(next.x, next.y);
User32Wrapper.GetCursorPos(ref current);
}
}
}
}
User32Wrapper.Move() is call to win32's mouse_event()
DLL_EXPORT void __cdecl Move(int x, int y) {
int _x = x * 65535 / GetSystemMetrics(0);
int _y = y * 65535 / GetSystemMetrics(1);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, _x, _y, 0, 0);
}
Am I missing something with mouse movement, any help would be appreciated.
Thanks

Unity Converting Microphone input into Hertz

I'm working on a Unity app that has some Microphone controls. At one point, I have to convert the Microphone input into Hertz (Hz) values and show them to the user. Now, I did some research and I made the following script for this purpose:
int amountSamples = 1024;
void Start ()
{
_fSample = AudioSettings.outputSampleRate;
}
void Update() {
if (focused && Initialized) {
if (Microphone.IsRecording(selectedDevice) && recording) {
spectrumData = GetSpectrumAnalysis();
if (spectrumCurve.keys.Length <= spectrumData.Length) {
float keyTimeValue = 0;
float currentHighestKeyTime = 0;
//create a curvefield if none exists
spectrumCurve = new AnimationCurve();
for (int t = 0; t < spectrumData.Length; t++) {
spectrumCurve.AddKey(1 / spectrumData.Length + t, spectrumData[t]);
spectrumCurve.MoveKey(1 / spectrumData.Length + t, new Keyframe(1 / spectrumData.Length + t, keyTimeValue = spectrumData[t])); //update keyframe value
if (keyTimeValue > currentHighestKeyTime) {
currentHighestKeyTime = keyTimeValue;
}
}
HighestKeyTimeValue = currentHighestKeyTime;
float freqN = HighestKeyTimeValue;
float f = freqN * (_fSample / 2) / amountSamples;
Debug.Log(f); //hz
}
}
}
audioSource.volume = 1;
}
And the GetSpectrumAnalysis()
public float[] GetSpectrumAnalysis ()
{
float[] dataSpectrum = new float[amountSamples];
audioSource.GetSpectrumData (dataSpectrum, 0, FFTWindow.BlackmanHarris);
for (int i = 0; i <= dataSpectrum.Length - 1; i++)
{
dataSpectrum[i] = Mathf.Abs (dataSpectrum[i] * sensitivity);
}
return dataSpectrum;
}
Now, with this code, the Hz value should be calculated in float f, it does work but the Hz values aren't too accurate, for example, I'm getting 400-500 Hz where I should get around 880 Hz. Similarly I'm getting 130 Hz instead of 220 Hz, etc.. So, I have 2 issues: I'm getting less Hz then I should and the Hz value is jumping too much and too fast so it's not consistent even if the sound playing is constant. Any idea how to improve this code? Where did I made a mistake?
EDIT
Check my answer for the solution.
Ok, nevermind, I found the solution, maybe this will help someone stumbling across this thread, change GetSpectrumAnalysis method to this:
public float test() {
float Threshold = 0.02f;
float[] dataSpectrum = new float[amountSamples];
audioSource.GetSpectrumData(dataSpectrum, 0, FFTWindow.BlackmanHarris); //Rectangular
float maxV = 0;
var maxN = 0;
for (int i = 0; i < amountSamples; i++) {
if (!(dataSpectrum[i] > maxV) || !(dataSpectrum[i] > Threshold)) {
continue;
}
maxV = dataSpectrum[i];
maxN = i; // maxN is the index of max
}
float freqN = maxN; // pass the index to a float variable
if (maxN > 0 && maxN < amountSamples - 1) { // interpolate index using neighbours
var dL = dataSpectrum[maxN - 1] / dataSpectrum[maxN];
var dR = dataSpectrum[maxN + 1] / dataSpectrum[maxN];
freqN += 0.5f * (dR * dR - dL * dL);
}
return freqN * (_fSample / 2) / amountSamples; // convert index to frequency
}
Then just call this in the update method like this:
Text.text = test().ToString("00");
For more info check out this thread: Unity answers

How to choose when fortune wheel stop in unity?

There is a lot of code about normal fortune wheels but I can't find anything about how to control where the wheel stops?
My fortune wheel should work like this: 1->5,8->12 have a 90% chance that the arrow stops there, 6 has a 1% chance, others have 9%.
And I need it to accumulate and increase during play, example: after 1 turn arrow have 2% chance of stop at 6 instead of 1%.
public List<int> prize;
public List<AnimationCurve> animationCurves;
private bool spinning;
private float anglePerItem;
private int randomTime;
private int itemNumber;
BoxCollider2D m_collider;
void Start()
{
spinning = false;
anglePerItem = 360/prize.Count;
}
void Update()
{
if (Input.GetKeyDown (KeyCode.Space) && !spinning) {
randomTime = Random.Range (1, 4);
itemNumber = Random.Range (0, prize.Count);
float maxAngle = 1080 * randomTime + (itemNumber * anglePerItem);
StartCoroutine (SpinTheWheel (5 * randomTime, maxAngle));
}
}
IEnumerator SpinTheWheel (float time, float maxAngle)
{
spinning = true;
float timer = 0.0f;
float startAngle = transform.eulerAngles.z;
maxAngle = maxAngle - startAngle;
int animationCurveNumber = Random.Range (0, animationCurves.Count);
Debug.Log ("Animation Curve No. : " + animationCurveNumber);
while (timer < time) {
//to calculate rotation
float angle = maxAngle * animationCurves [animationCurveNumber].Evaluate (timer / time) ;
transform.eulerAngles = new Vector3 (0.0f, 0.0f, angle + startAngle);
timer += Time.deltaTime;
yield return 0;
}
transform.eulerAngles = new Vector3 (0.0f, 0.0f, maxAngle + startAngle);
spinning = false;
Debug.Log( itemNumber);
Debug.Log ("Prize: " + prize [itemNumber]);//use prize[itemNumnber] as per requirement
}
First of all: Your percentages don't sum up
if as you describe 1..5 and 8..12 together cover 90% then there is only 10% left to share between 6 and 7.
So from your description each 1,2,3,4,5,8,9,10,11 and 12 would have an indivdual percentage of 9%. 6 would have 1% and since only 9% are left also 7 would have 9%.
I leave it at this for now but you probably will want to fix these weights according to your needs.
What you want then is a weighted random e.g. like
private Dictionary<int, int> weights = new Dictionary<int, int>
{
// Value | Weight TODO: Make sure these sum up to 100
{1, 9},
{2, 9},
{3, 9},
{4, 9},
{5, 9},
{6, 1},
{7, 9},
{8, 9},
{9, 9},
{10, 9},
{11, 9},
{12, 9}
};
// for storing the weighted options
private readonly List<int> weightedOptions = new List<int>();
private void Start()
{
// first fill the randomResults accordingly to the given weights
weightedOptions.Clear();
foreach (var kvp in weights)
{
// add kvp.Key to the list kvp.value times
for (var i = 0; i < kvp.Value; i++)
{
weightedOptions.Add(kvp.Key);
}
}
}
public int GetRandomNumber()
{
// get a random index from 0 to 99 (or how much items you have - 1)
var randomIndex = Random.Range(0, weightedOptions.Count);
// get the according value
return weightedOptions[randomIndex];
}
It is unclear how you then want to implement
after 1 turn arrow have 2% chance of stop at 12 instead of 1%
you can't increase the probability of one single number without decreasing the probability of one or all other numbers at the same time since they always sum up to 100%.
So first think about: Which number(s) should increase its(their) probability and which one(s) should decrease in exchange?
With your code
Now having your actual code I would modify it and implement before mentioned weighted list and then make the rotation pure angle based like
// instead of a Dictionary in order to be able to adjust the values via the Inspector
// (there are also more fancy solutions like SerializedDictionary with a special drawer for this purpose)
[Serializable]
public class WeightedValue
{
public int Value;
public int Weight;
public WeightedValue(int value, int weight)
{
Value = value;
Weight = weight;
}
}
// just a struct to get both the index and value at the same time
private struct RandomInfo
{
public readonly int Index;
public readonly int Value;
public readonly IReadOnlyList<int> WeightedOptions;
public readonly int AmountOfFullRotations;
public RandomInfo(List<int> weightedOptions, int minRotations, int maxRotations)
{
WeightedOptions = weightedOptions;
// get a random index
Index= Random.Range(0, WeightedOptions.Count);
// get the actual according value
Value = WeightedOptions[Index];
AmountOfFullRotations = Random.Range(minRotations, maxRotations);
}
}
public List<WeightedValue> PricesWithWeights = new List<WeightedValue>
{
// Value | Weight TODO: Make sure these sum up to 100
new WeightedValue(1, 9),
new WeightedValue(2, 9),
new WeightedValue(3, 9),
new WeightedValue(4, 9),
new WeightedValue(5, 9),
new WeightedValue(6, 1),
new WeightedValue(7, 9),
new WeightedValue(8, 9),
new WeightedValue(9, 9),
new WeightedValue(10, 9),
new WeightedValue(11, 9),
new WeightedValue(12, 9)
};
// minimum full rotations
// adjust in the Inspector
public int MinRotations = 2;
// maximum full rotations
// adjust in the Inspector
public int MaxRotations = 6;
// seconds one complete rotation shall take
// adjust in the Inspector
public float SpinDuration = 5;
// you can't assign this directly since you want it weighted
private readonly List<int> _weightedList = new List<int>();
private bool _spinning;
private float _anglePerItem;
private void Start()
{
_spinning = false;
_anglePerItem = 360f / PricesWithWeights.Count;
_weightedList.Clear();
// first fill the randomResults accordingly to the given wheights
foreach (var kvp in PricesWithWeights)
{
// add kvp.Key to the list kvp.value times
for (var i = 0; i < kvp.Weight; i++)
{
_weightedList.Add(kvp.Value);
}
}
}
private void Update()
{
// spinning is less expensive to check so do it first
if (!_spinning && Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(SpinTheWheel());
// or pass in a callback to actually do something with the random result
//StartCoroutine(SpinTheWheel(OptionalCallbackMethod));
}
}
//private void OptionalCallbackMethod(int result)
//{
// Debug.Log($"Congratulations! You just have won option {result}!")
//}
// Selects a random target option and a random amount of full spins before reaching the target
// then internally runs the parameterized overload
private IEnumerator SpinTheWheel(Action<int> onResult = null)
{
// What you had
//itemNumber = Random.Range(0, prize.Count);
// returns a random index .. not the actual value at this index
// this now has all information we need
var randomInfo = new RandomInfo(_weightedList, MinRotations, MaxRotations);
var itemNumberAngle = randomInfo.Index * _anglePerItem;
var currentAngle = transform.eulerAngles.z;
// reset/clamp currentAngle to a value 0-360 since itemNumberAngle will be in this range
while (currentAngle >= 360)
{
currentAngle -= 360;
}
while (currentAngle < 0)
{
currentAngle += 360;
}
// Now we can compose the actual total target rotation
// depends on your setup of course .. For my example below I will use it negative (rotation clockwise) like
var targetAngle = -(itemNumberAngle + 360f * randomInfo.AmountOfFullRotations);
Debug.Log($"Will spin {randomInfo.AmountOfFullRotations} times before ending at {randomInfo.Value} with an angle of {itemNumberAngle}", this);
Debug.Log($"The odds for this were {PricesWithWeights[randomInfo.Index].Weight / (float)PricesWithWeights.Sum(p => p.Weight):P} !");
yield return SpinTheWheel(currentAngle, targetAngle, randomInfo.AmountOfFullRotations * SpinDuration, randomInfo.Value, onResult);
}
// spins the wheel from the given fromAngle until the given toAngle within withinSeconds seconds
// using an eased in and eased out rotation
private IEnumerator SpinTheWheel(float fromAngle, float toAngle, float withinSeconds, int result, Action<int> onResult = null)
{
_spinning = true;
var passedTime = 0f;
while (passedTime < withinSeconds)
{
// here you can use any mathematical curve for easing the animation
// in this case Smoothstep uses a simple ease-in and ease-out
// so the rotation starts slow, reaches a maximum in the middle and ends slow
// you could also e.g. use SmoothDamp to start fast and only end slow
// and you can stack them to amplify their effect
var lerpFactor = Mathf.SmoothStep(0, 1, (Mathf.SmoothStep(0, 1, passedTime / withinSeconds)));
transform.localEulerAngles = new Vector3(0.0f, 0.0f, Mathf.Lerp(fromAngle, toAngle, lerpFactor));
passedTime += Time.deltaTime;
yield return null;
}
transform.eulerAngles = new Vector3(0.0f, 0.0f, toAngle);
_spinning = false;
Debug.Log("Prize: " + result);
// if provided invoke the given callback
onResult?.Invoke(result);
}

Normalize values to a range between -1 and 1

I want to process joystick values in such a way that the "normalized" values lay between -1 and 1 (including numbers with decimal places, for example 0.0129).
Specifically, I'm dealing with the input a Wiimote Controller stick.
The X axis has a range between 35 and 228, and the Y axis has range between 27 and 220.
The center for both is 128.
Now I would like to make it so that a value of 35 on the X axis would result in -1, the value of 128 should results in 0, and the value of 228 should result in 1.
Is there a special way to do with this?
The best I could come up with was:
public float[] GetStickNormalizedDataXY()
{
float[] ret = new float[2];
ret[0] = _stick[0];
ret[0] -= 35;
ret[1] = stick[1];
ret[1] -= 27;
for (int x = 0; x < 2; x++)
{
ret[x] /= 193f;
}
return ret;
}
But my results only vary between 0 and 1, so I guess I must be doing something wrong here.
This should do the trick:
float[] NormalizeStickData(float[] stickData)
{
return new[]
{
Normalize(stickData[0], 35, 228, 128, -1, 1, 0),
Normalize(stickData[1], 27, 220, 128, -1, 1, 0)
};
}
float Normalize(float value, float oldMin, float oldMax, float oldMid, float newMin, float newMax, float newMid)
{
if (value < oldMid)
return Interpolate(value, oldMin, oldMid, newMin, newMid);
else if (value > oldMid)
return Interpolate(value, oldMid, oldMax, newMid, newMax);
else
return newMid;
}
float Interpolate(float value, float oldMin, float oldMax, float newMin, float newMax)
{
return (float)(newMin + (newMax - newMin)*(value - oldMin)/(oldMax - oldMin));
}
Example
If you want to make a nice smooth function that takes on specific output values at 3 specific input values, the simplest thing to do is to use a quadratic polynomial. That means something of the form out=Ax^2 + Bx + C, where out is the output and x is the input. Plug in (x,out) = (35,-1), (128, 0), and (228,1) to get 3 equations, and solve for A, B, and C to get your x-axis mapping function.
If you'd like a more intuitive way to do exactly the same thing, then you can interpolate between 2 simpler linear functions like this:
float mapX(float x)
{
float xmin=35, xc=128, xmax=228;
// this line is correct for xmin and xc
float out1 -(x-xc)/(xmin-xc);
// this line is correct for xmax and xc
float out2 = (x-xc)/(xmax-xc);
// interpolate to use out1 at xmin and out2 at xmax
return out1 + (out2-out1)*(x-xmin)/(xmax-xmin);
}

How can I calculate and create positions of triangle shape?

private void FormationTriangle()
{
newpositions = new List<Vector3>();
for (int x = 0; x < squadMembers.Count; x++)
{
for (int y = x; y < 2 * (squadMembers.Count - x) - 1; y++)
{
Vector3 position = new Vector3(x, y);
newpositions.Add(position);
}
}
move = true;
formation = Formation.Square;
}
The loops are wrong. It put the squadMembers in one line one above the other.
Not even close to a triangle shape.
I want the squadMembers to stand in a triangle shape.
This is the moving part: But the problem is with the loops calculating the triangle shape positions. Other formations I did are working fine.
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers[i].transform.LookAt(newpositions[i]);
if (randomSpeed == true)
{
squadMembers[i].transform.position = Vector3.MoveTowards(
squadMembers[i].transform.position, newpositions[i], step[i]);
}
else
{
squadMembers[i].transform.position = Vector3.MoveTowards(
squadMembers[i].transform.position, newpositions[i], step[0]);
}
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) <
threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers[i].transform.rotation = Quaternion.Slerp(
squadMembers[i].transform.rotation, quaternion,
rotateSpeed * Time.deltaTime);
}
else
{
squadMembers[i].transform.rotation = Quaternion.Slerp(
squadMembers[i].transform.rotation, quaternions[i],
rotateSpeed * Time.deltaTime);
}
}
}
}
This answer will produce a triangle arranged like this:
x
x x
x x x
x x x x
x x x x x
Or, if there aren't enough to fill a full triangle:
x
x x
x x x
x x x x
x x x
Since you aren't guaranteed a perfectly triangular number of units, you should overestimate how big your triangle is, keep count of how many units you have placed, and then quit placing them when you reach your limit.
First, find the height of the smallest triangular number greater than your number of units, and that triangular number itself:
int height = Mathf.CeilToInt( (Mathf.Sqrt(8*squadMembers.Count+1f)-1f)/2 )
int slots = (int)(height * (height+1f)/2f)
Then, find the position of the first unit. We need to know how many rows of slots we have and how wide the bottom row of slots is:
float verticalModifier = 0.8f; // 0.8f to decrease vertical space
float horizontalModifier = 1.25f; // 1.25f to increase horizontal space
float width = 0.5f * (height-1f);
Vector3 startPos = new Vector3(width* horizontalModifier, 0f, (float)(height-1f) * verticalModifier);
Then, add until you've added enough
int finalRowCount = height - slots + squadMembers.Count;
for (int rowNum = 0 ; rowNum < height && newpositions.Count < squadMembers.Count; rowNum++) {
for (int i = 0 ; i < rowNum+1 && newpositions.Count < squadMembers.Count ; i++ ) {
float xOffset = 0f;
if (rowNum+1 == height) {
// If we're in the last row, stretch it ...
if (finalRowCount !=1) {
// Unless there's only one item in the last row.
// If that's the case, leave it centered.
xOffset = Mathf.Lerp(
rowNum/2f,
-rowNum/2f,
i/(finalRowCount-1f)
) * horizontalModifier;
}
}
else {
xOffset = (i-rowNum /2f) * horizontalModifier;
}
float yOffset = (float)rowNum * verticalModifier;
Vector3 position = new Vector3(
startPos.x + xOffset, 0f, startPos.y - yOffset);
newpositions.Add(position);
}
}
Let's see what the list of positions contains for a simple value, n = 3
First, loop x from 0 to 2 (3 - 1)
Then for each x, loop from x to 4-x (3*2 - x - 1 - 1)
Remembering that a<b is the same as a<=b-1
That gives us...
0,0
0,1
0,2
0,3
0,4
1,1
1,2
1,3
2,2
Which is a lot of positions. Certainly more than 3 units can occupy! At least it is a triangle:
X\Y 0 1 2 3 4
0 # # # # #
1 # # #
2 #
The main problem is that you're generating way more positions than needed and expecting to fill it somehow.
You need to calculate your width and height based on the area formula for a triangle: A = (b*h)/2 and you may even want b=h, where A = number of units.
So, something like this:
int b = Mathf.CeilToInt(Mathf.Sqrt(squadMembers.Count));
for (int x = 0; x < b; x++)
{
//the divide by 2 is accounted for with this 2*
for (int y = x; y < 2 * (b - x) - 1; y++)
{
Vector3 position = new Vector3(x, y);
newpositions.Add(position);
}
}

Categories