Run a function as far as a variable reaches specific values - javascript

I have a canvas game which calls a function incScore every time an action is performed in the game to increase the score.
Inside incScore I have a few if statements to draw a particular image to represent a level number on the canvas.
I also want to have a sound play once per level up. The way I've gone about things the lvlup sound will play every time the score matches the if statement.
Can anyone please help me get this so that the sound will only play once when the level changes and not again until the next level change? I'm also mention I'm using jQuery incase it has anything that could help me.
incScore(); //everytime an action in the game causes the score to increase
function incScore(){
if (scoreTotal < 500){
lvlimg = "L01";
drawLevel(lvlimg);
lvlupSound();
}
else if (scoreTotal > 500 && scoreTotal < 1000){
lvlimg = "L02";
drawLevel(lvlimg);
lvlupSound();
}
else{
lvlimg = "L03";
drawLevel(lvlimg);
lvlupSound();
}
}

You could shorten your function and use a semi static property to save the state. Using that, you can compare the current level to the previous and play a sound if they differ.
function incScore(){
incScore.level = incScore.level || 'L0'; //< initialize property
lvlimg = "L0" + scoreTotal < 500 ? 1 : scoreTotal < 1000 ? 2 : 3;
drawLevel(lvlimg);
if (incScore.level!=='L0' &&
incScore.level !== lvlimg) { lvlupSound(); };
// ^compare local level to current
incScore.level = lvlimg;
// ^ update local level
}
[edit, based on comment] The third line is a so called ternary, or conditional operator. See MDN. You can use more conditions.
To avoid playing a sound before the score has reached a first level, you could use
if (incScore.level!=='L0' && incScore.level !== lvlimg).
I've created a mockup jsFiddle

A simple solution could be comparing the current level to the old one, to detect when the level changed:
function scoreToLevel(score)
if(score < 500){
return 1
}else if (score < 1000){
return 2
}else{
return 3
}
}
function incScore()
var next_level = scoreToLevel(scoreTotal)
if(next_level !== current_level){
lvlimg = "L0" + next_level;
drawLevel(lvlimg)
lvlupSound()
}
}

The easiest solution is to factor the sound out of those if statements. If the levels are nice and regular like that(every 500 points) and the points always increase in a way that you will always land exactly on an even multiple of 500 when you level up, something like this should do:
if(scoreTotal % 500 === 0 && scoreTotal < 1001)
{
lvlupSound();
}
If you won't always land directly on the gate to the next level(maybe the player can earn anywhere between 1 and 15 points at a time) then you should be able to get by using something along the lines of this before you increment the player's score:
if( (scoreTotal % 500) > ((scoreTotal + increment) % 500)
{
lvlupSound();
}
if your level boundries are not regular like that obviously it gets a little bit more complex, but that should get you started.

That is because you have the in every statement for every score (which means from 0 to infinite).
You will need to write inner if statements such as;
if (scoreTotal < 500){
lvlimg = "L01";
drawLevel(lvlimg);
if(scoreTotal x times of each the level) // That means for each level completed
{
lvlupSound();
}
}

If your score increment is only 1, then only play the tone when the score equals the threshold for a new level.
If they can increase their score by more than 1, then you could pass the number of points in and check the score before and after to see if the numbers fall on each side of the threshold.
If that still doesn't work, some more info on the "level" and points would be appreciated.

Try something like this (demo):
var scoreTotal,
lastLevel = 0,
levels = [500, 1000, 2500, 5000, 10000, 25000, 50000, 75000],
currentLevel = 0,
lvlImg;
function incScore() {
while (scoreTotal > levels[currentLevel]) {
currentLevel++;
}
if (lastLevel !== currentLevel) {
lastLevel = currentLevel;
// gives a two digit number with a leading zero
lvlImg = ('0' + currentLevel).slice(-2);
drawLevel("L" + lvlimg);
lvlupSound();
}
}
Then you can easily add additional levels by adding the score cutoff to the levels variable.

Related

Count how many times document.hasFocus returns "true", if it's equal or greater than 15 do something

I'm using setInterval to check every second for document.hasFocus(), so every second it returns true or false.
setInterval(function() {
console.log(document.hasFocus());
}, 1000);
When the return of true is equal to or greater than 15, I would like to do something. I wrote the code below (which obviously doesn't work) to make it easier to understand what I want to achieve.
if (document.hasFocus()) ≥ 15 {
do something
}
Could anyone help me with this?
document.hasFocus() returns a boolean not a number.
So you can use a counter and increment it each time document.hasFocus() returns true.
Then you can test the counter.
As far as I can see, you just want to count for 15 times before calling some function or executing some logic, the variable that is responsible for counting represents your application state. So we will call it count
let count = 0;
then we just increment count while the condition is met every second, until finally we stop the interval when the condition is met
let intervalToken = setInterval(function checkDocument(){
if(document.hasFocus()){
count = count + 1;
if(count > 15){
clearInterval(intervalToken); // stop intervale here
callMyFunction(); // call your function here
}
}
}, 1000)
Simply add a counter like this:
let counter = 0;
if (document.hasFocus() && ++counter >= 15) {
alert('you did it!');
}
You just need a variable to count the number of times document.hasFocus().
let focusCount = 0;
setInterval(() => {
focusCount = document.hasFocus() ? focusCount + 1 : focusCount;
if (focusCount >= 15) {
console.log(focusCount);
}
}, 1000);

How do you set a random probability of a object appearing on screen javascript (for a game)

I want an item to appear every now and then. Usually one item appear that give +10 score but i want a rare item to appear at a random chance that give more points.
Ive already tried something that looks like this except repurposed for what i need
if (Math.random() * 100 < 80) {
sendMessage("hi");
}
else if (Math.random() * 100 < 5) {
sendMessage("bye");
}
I expected for a gold thing to appear but it never did
You should probably first determine what you want the probabilities of these events to be and how frequently you want them to occur.
Presumably you'd have some short time interval between these events, say for example 60 seconds. And then the chance of a rare one could be 5%. So your example at the moment seems to be a bit off because of your else statement.
const rand => Math.rand() * 100;
if(rand() <= 80){
// 80% of any reward appearing
if(rand() <= 5){
// Give the super reward, 5% chance
} else {
// Give the regular reward
}
}
Hope that makes sense.
basically you need to calculate once the random probability and then assign the reward based on your logic.
const normalProbability = 80;
const epicProbability = 5;
const obtainReward = () => {
const calculated = Math.random() * 100;
if (calculated <= normalProbability && calculated > epicProbability) {
console.log(`normal probability to appear a common object... you got ${calculated}%`)
return calculated;
} else if (calculated <= epicProbability) {
console.log(`epic probability to appear a common object, you got ${calculated}%`);
return calculated;
}
console.log(`no reward because you got... ${calculated}%`);
return calculated;
}
console.log("starting the automatic process")
setInterval(() => obtainReward(), 3000);

Javascript wait for one animation to finish before the next one begins

I have a Javascript function (Not jQuery) that animates a box opening or closing. The problem is that I close the box, run some code that changes the content, and then reopen it.
Now the "problem" is that the rest of the code is too fast, and so it never even manages to close, let alone reopen. I could make the animation not be allowed to run again internally unless the last one was finished, but this would limit it if I say, were to want to run it twice on two different objects.
So what's the best method to prevent this? My thought was possibly a timeout that says to wait before running the animation, but that seems hacky, an I wasn't sure if there was a better solution?
Thanks.
function animate(boxID, step, limit, speed){
// Add timeout property to animate function/object if it doesn't exist already
if(animate.timeout == undefined) animate.timeout = 0;
// Clear the current timeout
clearTimeout(animate.timeout);
// Initialize box and current box height
var box = document.getElementById(boxID);
var h = box.clientHeight;
// Check if we want the step needs to be changed to pos/neg based on which direction is wanted to be going
if(h < limit && step < 0 || // Positive
h > limit && step > 0){ // Negative
step *= -1;
}
// If the step is positive, then we need to be below the limit, or if negative, then greater than the limit
if((step > 0 && h <= limit - step) || (step < 0 && h >= limit - step)){
// Set new height
box.style.height = h + step + "px";
// Start new timeout
animate.timeout = setTimeout(function(){ animate(boxID, step, limit, speed, 1); }, speed);
}
else{
box.style.height = limit + "px"; // Set to the exact height
}
}
You could achieve this with a callback. Your animate function gets a plus parameter, a function to call when the animation is ready:
function animate(boxID, step, limit, speed, onReady){
When the animation is done, you call it:
else{
box.style.height = limit + "px"; // Set to the exact height
if (onReady) { onReady(); }
}
You also want to forward the callback to the timeout call:
setTimeout(function(){ animate(boxID, step, limit, speed, 1, onReady); }, speed);
So, you can call the function for multiple boxes like this:
animate(box1_id, close_step, close_limit, close_speed, function () {
// now box1 is closed, put something in. then:
animate(box1_id, open_step, open_limit, open_speed, null);
});
// then the same for box2, etc…
This way box1 and box2 will close simultaneously, and only reopen after the thing have been put inside.
Also, you can't store the timer on the function, because now it's running on multiple boxes. So you may store it on the boxes, or a separate place instead. For example create an object outside of the function and put all the boxes' timers in that:
var timeouts = {};
timeouts[box1_id] = setTimeout(…);

JavaScript code for a short computer task

I'm very new to JavaScript programming and would appreciate any sort of help on this.
I'm trying to program a task that is separated into 4 games. Each of the games has the same structure- a stimulus (different image in each game) appears on the screen, then you have 2 choices- either 1) press “enter” to keep playing to see the “result” image (different image in each game) or 2) press “space bar” stop playing and accumulate the points earned up to that point. If you decide to keep playing, the “result” image will show up which will show you whether you win or lose one point. You start the game with 50 points and over 100 trials, the percentage of “win”s per 10 trials drops from 90% to 0%.
What is the best way to set up these trials and collect this type of data? Currently, I have made an array of 100 responses corresponding to the 100 trials:
correctresponse = [“spacebar_code”, “enter_code”, “enter_code”, “enter_code” ….]
Similarly, I’ve made an array of 100 outcome responses:
outcome = [“image1.jpg”, “image2.jpg”…]
I’m also unsure as to setting up some key functions. My function to capture keypresses right now is:
function getKeyTest(keyStroke) {
isNetscape=(document.layers);
keyID = (window.event) ? event.keyCode : keyStroke.keyCode;
eventChooser = (isNetscape) ? keyStroke.which : keyID;
which = String.fromCharCode(eventChooser);
for (i=0;i<response_keys.length;i++) {
if (which == response_keys[i]) {
rtEndTime = new Date();
var currentRT = rtEndTime - rtStartTime;
trialRTs.push(currentRT);
if (currentRT < 500)
rtUnder500++;
} else if (currentRT > 10000) {
rtOver10000++;
}
if (response_keys[i] == correctResponses[trialNumber]) {
accuracy[trialNumber] = 1;
correctTotal = correctTotal + 1;
} else {
accuracy[trialNumber] = 0;
}
trial_outcome();
}
if (which == spacebar_code && trialTypes[trialNumber] == "test") { //this does not seem to end the trial
save_data(); }
}
}
Another question I have is:
There are 4 different conditions that have to be randomized across the 4 games.
cue, delay;
cue, no delay;
no cue, delay;
no cue, no delay.
Where
Cue = there’s a tally of points shown above the stimuli
Delay = there is a 5-second delay between the outcome of each trial and the next chance for the person to respond
I’m not quite sure what the best way to randomize the 4 conditions across the 4 games would be. I think I need to create an array and do a shuffle function, but how would I define the elements in that array to contain the two different aspects (cue and delay)?

My method fires when it shouldn't

This code seems to loop through adding 1 to player1.score untill the score is === to whatever i put in the second if statement. Anyone know why?
pointScored: {
startNextSet: function(Scorer) {
if (gameController.bananasTaken < 3 && Scorer === "player1") {
console.log(gameController.player1.score);
gameController.player1.score += 1;
if (gameController.player1.score === 10 &&
gameController.bananasTaken === 0 &&
gameController.player1.bananaCount === 0) {
console.log(gameController.player1.score);
gameController.updatePlayerStats(gameController.Banana1, 20, gameController.canvas.height
- 20 - gameController.Banana1.height, gameController.player1, "left");
console.log("player 1's first point");
}
I'm currently learning about using a debugger but thought i'd leave this here to see if anyone knows why. Thanks.
There's a chance your values get evaluated as strings. The === operator doesn't do any type conversions, that's why its faster.
Consider changing your evaluation to use ==. The same issue has cropped up in another question.
I have refactored your code a bit & used the == notation I suggest above. Please try running it and tell me if it works.
pointScored:{
startNextSet: function(Scorer) {
gc=gameController; //to save thy fingers from typing ache
if (gc.bananasTaken > 2 || Scorer !== "player1")
return;
console.log(gc.player1.score); // this logs 6 times from 0 to 5
gc.player1.score += 1;
if (gc.player1.score == 5 && gc.bananasTaken == 0) {
alert(gc.player1.score); //*******!
if(gc.player1.bananaCount == 0) {
gc.updatePlayerStats(gc.Banana1, 20, gc.canvas.height - 20 - gc.Banana1.height, gc.player1, "left");
console.log("player 1's first point");
}
}
}
}
As I look at your function, it seems that this logic needs to be INSIDE the gameController object.

Categories