Score system based on time NAN - actionscript-3

I've created a score system in my flash Quiz game where the faster you answer a question, the more points you get. At the moment however my tracer shows 'NAN' when I run my game. Can anybody see why this is?
var score:int = 0;
var count:int = 0;
var mTimer:Timer;
mTimer = new Timer(100, 70);
function processScore():void {
var count:int = mTimer.currentCount;
var score:int = score + (700 - (count * 10));
trace("score registered");
}
trace(aUserAnswers[numLoops] + " " + returnedNumber);
if(aUserAnswers[numLoops] == returnedNumber){
processScore();
}
returnedNumber is when a button is clicked, if the number matches that which is in the array, the question is correct.
Thank you

You're redeclaring count and score inside processScore(). That makes them local variables to the function, unrelated to the previous declared variables of the same name. This means that their values are lost when the function finishes and the previous variables are unchanged. I'm guessing that at some point you divide something by one of them and since you'll always be dividing by zero you get NAN.

Related

How to use the Timestamp data from JSON file in Unity

I'm currently working with a folder of JSON files which are collected through a tracking experiment with a drone. The data contains position, rotation and timestamp of the drone while it's moving and levitating inside the tracking system.
What I'm currently doing is trying to simulate the movement of the drone inside Unity using those data. So far, I've managed to parse the position and rotation from the data to an object inside Unity and extract the timestamp to System.DateTime in Unity.
However, I don't how to work with the timestamp. I want to use the timestamp to match the position and rotation of the object (i.e: at this timestamp, the drone should be at this position(x,y,z) and has the rotation(x,y,z,w)). Can someone help me with this problem, really appreciate your help :D Here is my current code:
void Update()
{
if (loaded)
{
for(int i = 0; i <= pos_data.Count; i+= 10)
{
Cube.transform.position = pos_data[i];
Cube.transform.rotation = rot_data[i];
}
}
else
{
LoadJson();
//startTime = datetime[0];
loaded = true;
}
}
public void LoadJson()
{
string HeadPath = #Application.dataPath + "/Data/" + "drone_data_1.json";
string HeadJsonhold = File.ReadAllText(HeadPath);
var data_ = JSON.Parse(HeadJsonhold);
for (int rows = 0; rows <= data_.Count; rows += 10)
{
pos_data.Add(new Vector3(data_[rows]["location"]["x"].AsFloat, data_[rows]["location"]["y"].AsFloat, data_[rows]["location"]["z"].AsFloat));
rot_data.Add(new Quaternion(data_[rows]["rotation"]["x"].AsFloat, data_[rows]["rotation"]["y"].AsFloat, data_[rows]["rotation"]["z"].AsFloat, data_[rows]["rotation"]["w"].AsFloat));
Time = System.DateTime.ParseExact(data_[rows]["Timestamp"], "yyyyMMddHHmmss",null);
//Debug.Log(Time);
}
}
If I understand you correctly what you are getting are samples of a real-world drone that at some rate stores keyframes of its movement.
Now you already successfully load that json data but wonder how to animate the Unity object accordingly.
The timestamp itself you can't use at all! ^^
It most probably lies somewhere in the past ;) And you can't just assign something to Time.
What you can do, however, is take the timestamp of the first sample (I will just assume that your samples are all already ordered by the time) and calculate the difference to the next sample and so on.
Then you can use that difference in order to always interpolate between the current and next sample transforms using the given time delta.
Currently you are just doing all samples in one single frame so there won't be any animation at all.
Also just as sidenote:
for(int i = 0; i <= pos_data.Count; i+= 10)
is wrong twice:
a) you already skipped 10 samples when loading the data -> are you sure you now want to again skip 10 of these => In total every time skipping 100 samples?
b) since indices are 0 based the last accessible index would be pos_data.Count - 1 so in general when iterating Lists/arrays it should be i < pos_data.Count ;)
First of all I would suggest you use a better data structure and use one single list holding the information that belongs together instead of multiple parallel lists and rather load your json like e.g.
[Serializable]
public class Sample
{
public readonly Vector3 Position;
public readonly Quaternion Rotation;
public readonly float TimeDelta;
public Sample(Vector3 position, Quaternion rotation, float timeDelta)
{
Position = position;
Rotation = rotation;
TimeDelta = timeDelta;
}
}
And then
// Just making this serialized so you can immediately see in the Inspector
// if your data loaded correctly
[SerializeField] private readonly List<Sample> _samples = new List<Sample>();
public void LoadJson()
{
// start fresh
_samples.Clear();
// See https://learn.microsoft.com/dotnet/api/system.io.path.combine
var path = Path.Combine(Application.dataPath, "Data", "drone_data_1.json");
var json = File.ReadAllText(path);
var data = JSON.Parse(json);
DateTime lastTime = default;
for (var i = 0; i <= data.Count; i += 10)
{
// First I would pre-cache these values
var sample = data[i];
var sampleLocation = sample["location"];
var sampleRotation = sample["rotation"];
var sampleTime = sample["Timestamp"];
// Get your values as you did already
var position = new Vector3(sampleLocation["x"].AsFloat, sampleLocation["y"].AsFloat, sampleLocation["z"].AsFloat));
var rotation = new Quaternion(sampleRotation["x"].AsFloat, sampleRotation["y"].AsFloat, sampleRotation["z"].AsFloat, sampleRotation["w"].AsFloat));
var time = System.DateTime.ParseExact(sampleTime, "yyyyMMddHHmmss", null);
// Now for the first sample there is no deltaTime
// for all others calculate the difference in seconds between the
// last and current sample
// See https://learn.microsoft.com/dotnet/csharp/language-reference/operators/conditional-operator
var deltaTime = i == 0 ? 0f : GetDeltaSeconds(lastTime, time);
// and of course store it for the next iteration
lastTime = time;
// Now you can finally add the sample to the list of samples
// instead of having multiple parallel lists
_samples.Add(new Sample(position, rotation, deltaTime));
}
}
private float GetDeltaSeconds(DateTime first, DateTime second)
{
// See https://learn.microsoft.com/dotnet/api/system.datetime.op_subtraction#System_DateTime_op_Subtraction_System_DateTime_System_DateTime_
var deltaSpan = second - first;
// See https://learn.microsoft.com/dotnet/api/system.timespan.totalseconds#System_TimeSpan_TotalSeconds
return (float)deltaSpan.TotalSeconds;
}
So now what to do with this information?
You now have samples (still assuming ordered by time) holding all required information to be able to interpolate between them.
I would use Coroutines instead of Update, in my eyes they are easier to understand and maintain
// Do your loading **once** in Start
private void Start()
{
LoadJson();
// Then start the animation routine
// I just make it a method so you could also start it later e.g. via button etc
StartAnimation();
}
// A flag just in case to avoid concurrent animations
private bool alreadyAnimating;
// As said just making this a method so you could also remove it from Start
// and call it in any other moment you like
public void StartAnimation()
{
// Only start an animation if there isn't already one running
// See https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
if(!alreadyAnimating) StartCoroutine(AnimationRoutine());
}
private IEnumerator AnimationRoutine()
{
// Just in case abort if there is already another animation running
if(alreadyAnimating) yield break;
// Block concurrent routine
alreadyAnimating = true;
// Initially set your object to the first sample
var lastSample = _samples[0];
Cube.transform.position = lastSample.Position;
Cube.transform.rotation = lastSample.Rotation;
// This tells Unity to "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
// then iterate through the rest of samples
for(var i = 1; i < _samples.Count; i++)
{
var lastPosition = lastSample.Position;
var lastRottaion = lastSample.Rottaion;
var currentSample = _samples[i];
var targetPosition = sample.Position;
var targetRotation = sample.Rotation;
// How long this interpolation/animation will take
var duration = currentSample.TimeDelta;
// You never know ;)
// See https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html
if(Mathf.Approximately(duration, 0f))
{
Cube.transform.position = targetPosition;
Cube.transform.rotation = targetRotation;
lastSample = currentSample;
continue;
}
// And this is where the animation magic happens
var timePassed = 0f;
while(timePassed < duration)
{
// this factor will be growing linear between 0 and 1
var factor = timePassed / duration;
// Interpolate between the "current" transforms (the ones it had at beginning of this iteration)
// towards the next sample target transforms using the factor between 0 and 1
// See https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
Cube.transform.position = Vector3.Lerp(lastPosition, targetPosition, factor);
// See https://docs.unity3d.com/ScriptReference/Quaternion.Slerp.html
Cube.transform.rotation = Quaternion.Slerp(lastRotation, targetRotation, factor);
// This tells Unity to "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
// increase by the time passed since the last frame was rendered
timePassed += Time.deltaTime;
}
// just to be sure to end with clean values
Cube.transform.position = targetPosition;
Cube.transform.rotation = targetRotation;
lastSample = currentSample;
}
// Allow the next animation to start (or restart this one)
alreadyAnimating = false;
// Additional stuff to do once the animation is done
}

AS3 variables from array not updating, even when given direct "command"

I hope you're well.
I've a problem that I do not understand. My variables seem to update but they don't (i've explained further in context in the code below).
[I'm trying to create a store where my users can buy bosses for my game].
I really hope you can help me :)
Here is the code. Do not hesitate to ask me for further details (number total of bosses will be around 36).
var bossBuyBtnArray:Array=[campagnesBuy.boss1, campagnesBuy.boss2];
//array with the MovieClips used as button to buy
var pbArray:Array=[campagnesBuy.pb1, campagnesBuy.pb2]; //array with MovieClips that tells if boss is owned or not
var bossVar:Array=[sp[59], sp[60]]; //array with int variables needed to save the state of inventory
for each(var storeBtn: MovieClip in bossBuyBtnArray)
storeBtn.addEventListener(MouseEvent.CLICK, fnBossStoreBtn); //when user click on the store buttons,
function fnBossStoreBtn(e: Event): void {
var listElementBoss: DisplayObject = e.target as DisplayObject; //listeElementBoss = Selected Boss.
var iBoss: int = bossBuyBtnArray.indexOf(listElementBoss); //get the index of the selected boss
if (iBoss < 0) { //check if selected boss is in the array
listElementBoss = e.currentTarget as DisplayObject;
if (listElementBoss) iBoss = bossBuyBtnArray.indexOf(listElementBoss);
}
if (iBoss < 0) return;
if(pbArray[iBoss].currentFrame == 1){ //check if boss not already owned.
if (sp[58]>999){ //check if user has enough gold.
/*The Part that Doesn't Work : */
bossVar[iBoss] = 1; //modify the variable (sp[59] or sp[60])
//normally it would update the variable, let's say sp[59] if boss 1 is selected.
//The interface is modified by fnAlreadyOwned (see below) so I guess something is updated,
//But when I trace(sp[59]), it says 0, and the writeObject() function saves 0. Meaning that
//when I reload the game, the gold is gone, but boss is locked again.
/*The rest works*/
sp[58] = sp[58] -1000; //substract cost of the boss from the total gold.
gameMode.pirateTxt.text = sp[58]; //update gold "inventory"
writeObject(); //save the state of gold and boss ownership
fnAlreadyOwned(null);// updates the interface
}else{
trace("not enough gold"); //if not enough gold
}
}else{
trace("already owned"); //if boss already owned.
}
}
When you do this:
var bossVar:Array=[sp[59], sp[60]]; //array with int variables needed to save the state of inventory
you take the variables from the sp array and put them in bossVar array. int variables are copied in this case.
That means changing either sp[59] or bossVar[0] won't change the other.
example code:
var a:Array = [1, 2];
var b:Array = [a[0], 5];
a[0] = 42;
trace("a[0] =" + a[0]);
trace("b[0] =" + b[0]);
You could circumvent that by storing objects for example.

AS3 - Go to random 5 frames out of 7

Hello and thank you so much for your time.
I'm trying to build a quiz for my students, where the start button will go to a random frame out of 7. Then on the landing frame, question appears and the answer is selected via radiobutton then submitted via another button which goes to the next random question. This needs to happen 5 times so it will pick 5 questions randomly out of 7 and not repeating any previous question. If anyone can point me to right direction, it'll be much appreciated.
//Start Button - AS3 Frame #8157
startBtn.addEventListener(MouseEvent.CLICK, startQuiz);
function startQuiz(event:MouseEvent):void{
}
//Submit Button with score count - AS3 Frame #8158
var count:Number = 0;
var mygroup1:RadioButtonGroup = new RadioButtonGroup("group1");
q1a1.group = q1a2.group = q1a3.group = q1a4.group = q1a5.group = mygroup1;
b1.addEventListener(MouseEvent.CLICK, quizHandler1)
function quizHandler1(event:MouseEvent):void{
if(mygroup1.selection.label=="B) 12") {
count = count + 20;
scoreresult.text = (count).toString();
var number_array:Array = [8158,8159,8160,8161,8162,8163,8164 ];
var final_array:Array = [];
var count_selected:int = 5;
var i:int;
for(i = 0; i < count_selected; i++)
{
if(number_array.length == 0)
break;
else
final_array.push(number_array.splice(Math.floor(Math.random() * number_array.length), 1)[0]);
}
trace(final_array);
}
}
Since you don't want to repeat the same value, you need to know what values you've used already. There's a bunch of ways you could do this, but probably the most straight forward is to put all your values in an array, then remove a random value until the array is empty. Here's an example:
// create an array with all the frames you want to visit
var frames:Array = [0, 1, 2, 3, 4, 5, 6];
// when you want to pick one randomly, remove it using splice
var frame:int = frames.splice(Math.random() * frames.length, 1)[0];
// when the array is empty, you've visited every frame
if(frames.length == 0)
trace("all done!");
Here's the docs on splice(): http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#splice%28%29

AS3 - Using a For Loop to Update Multiple Points and Their Values in an Array

I'm a bit new with AS3 (but not really with coding) so please forgive my ignorance. I'm creating a small function that will be called by a Main Function to update the position of 52 Pointers that have the x and y position of multiple point objects (empty movie clips). It will also then update two global arrays with those values (one array for the x and one for the y).
The problem is, as there are 52 of them, and they will probably grow in quantity, I'd like to be able to use a FOR function to do it, but I can't seem to be able to figure it out.
I get this error: Access of undefined property _point.
Here is a piece of the code that dream about:
function happyFunc():void
{
var avpointers:int = 52;
var vpointx:Array = new Array();
var vpointy:Array = new Array();
for (aa=0; aa<vpointers; aa++)
{
vpointx[aa] = _point[aa].x;
vpointy[aa] = _point[aa].y;
}
}
And this is the code that I'm stuck with...
function reallySadFunc():void
{
_point1 = localToGlobal(new Point(point1.x,point1.y));
//...
_point52 = localToGlobal(new Point(point52.x,point1.y));
vpointx[0] = _point1.x;
vpointx[1] = _point2.x;
//...
//oh god there are 104 lines of this why do I have to suffer
}
Thank you!
If I read your question correctly, I think this is what you need:
public static const CLIP_COUNT:int = 52;
// ...
private function happyFunc(parentClip:DisplayObjectContainer):void
{
var vpointx:Vector.<Number> = new Vector.<Number>(CLIP_COUNT, true);
var vpointy:Vector.<Number> = new Vector.<Number>(CLIP_COUNT, true);
var clipPoint:Point = new Point ();
var globalPoint:Point;
for (var i:int = 0; i < CLIP_COUNT; i++)
{
var childClip:DisplayObject = parentClip.getChildByName("point" +
(i + 1).toString());
clipPoint.x = childClip.x;
clipPoint.y = childClip.y;
globalPoint = parentClip.localToGlobal(clipPoint);
vpointx[i] = globalPoint.x;
vpointy[i] = globalPoint.y;
}
// do something with vpointx and vpointy - they're local variables
// and will go out of scope unless you declare them as class members
// or somehow return them from this function.
}
This function works by taking the parent display object (the one that contains the 52 movie clips - this could be the Stage) and iterates through them by getting each movie clip by name. The code assumes that your movie clips are called point1, point2, ..., point52.
To speed up the local-to-global coordinate conversion, two Point objects are created and then reused during the for loop to avoid creating many temporary Point instances.
Instead of using Array, use Vector.<Number> - this class has better performance than Array does.

AS3 seeing every score

I have recently taken up programming and encountered a problem when it comes to score display. The score has not problem incrementing and displaying it is just that as the score updates it does not remove the last score. After a dozen frames I have a jumble of scores displayed. I have spent a few days google searching to see if I could find any type of answer but not seen a problem similar to this.
My Code:
public function balldistance(event:Event){ // function called on ENTER_FRAME in order to update the distance of the ball object
var txt:TextField = new TextField();
txt.text = "Distance: " + String(balldist);
txt.x = 25;
txt.y = 25;
addChild(txt);
trace(balldist); // I added this line in my code for troubleshooting purposes just so I could see the balldist augment.
balldist += Ball5.dx; // I am having the score(balldist) augment based on the distance the ball has traveled from its starting point.
}
A friend of mine suggested a removeChild(txt) but when i tried this it did not show the score updating.
Thank you
It looks like you are creating a new txt:TextField EVERY time ENTER_FRAME is triggered.
Try declaring/initializing it once, outside of that listener function:
var txt:TextField = new TextField();
txt.x = 25;
txt.y = 25;
addChild(txt);
Then on enter frame reference the SAME txt TextFeild instance, instead of making a new one over and over again:
public function balldistance(event:Event){
txt.text = "Distance: " + String(balldist);
balldist += Ball5.dx;
}