I am programming a video game, and in it, I would like to call a method that adds a player bonus life for every 2,000 points scored. I have no idea what operator(s) to use for such a thing.
If (score is divisible by 2000, each increment){
DoSomething();
}
I'm not even sure if I'm asking this question correctly. Basically when the player scores 2,000pts, 4,000pts, 6,000pts, etc, I want to give him/her a bonus life by calling a method. I already have the method created; I was just wondering how I can apply the conditions that call it.
I tried using this:
public int bonusTarget = 2000;
paddle = GameObject.Find("Paddle").GetComponent<Paddle>();
if(score >= bonusTarget){
paddle.Bonus();
bonusTarget += 2000;
}
but, it awarded more than one bonus life each increment. I need to award the bonus life only one time for each 2,000pts
"score is divisible by 2000"
if (score % 2000 == 0) DoSomething()
If You need to track score use property instead of varilable eg.:
private int _score;
public int Score
{
get
{
return _score;
}
set
{
var a = Math.Floor(_score / 2000)
var b = Math.Floor(value / 2000)
if (a < b) DoSomething();
_score = value;
}
}
Put the if(score is divisible by 2000) check inside the DoSomething() method.
There's no reason to keep this outside the method that increases the bonus lives, in the dozens of possible code paths that increase the score. You need ONE CENTRAL location that the score increases, and checks various conditions.
You need a centralized location to add to the score, like this:
public void AddToScore(int points)
{
score += points;
if (score >= bonusTarget)
{
bonusTarget += 2000;
paddle.Bonus();
}
}
I switched the two lines inside the conditional block in case paddle.Bonus() tries adding points itself, which could cause issues, possibly even infinite recursion.
If you make it so you always add to the score using this method, you can add any special handling you want. You might also want to write a ResetScore() and a GetScore() so you never access score directly elsewhere in your code.
Related
I've been working on a slotmachine in C# for practise purposes, and the machine itself works as intentional. The points system, however, does not. The game starts at 100 points, and if, for example, the player lose three 5-point bets and wins 40 points on the fourth bet, the expected points would be 100-20+40=120 points. For some reason however, the code treats ALL the previous bets as being 40 point wins as well, bringing the total to 100-20+160=240 points. If the player then lose the fifth 5-point bet, the score jumps to 75.
I start by setting the 'points' value to 100, which should then update everytime the 'game()' function is called upon.
public static void Main()
{
int points = 100;
int num = 0;
Console.WriteLine("Welcome to 'Slotmachine'!\nThe aim of this game is to get a score of 1000 or higher.\nYou lose if you reach 0 or lower.\nPress enter to play");
Console.ReadLine();
Console.Clear();
points = points + game(100);
while(points<1000 && points>0)
{
num = num + 1;
Console.WriteLine("You've played for "+num+" number of round(s)");
points = points + game(points);
}
}
The 'game()' funtion returns the players winnings, which is used to update the 'points' value (Suspect nr 1?).
Inside the game function I have a 'usrbet' which takes an input from the user (1-10), which is then fed into the 'slots()' function to determine the winnings (the 'points' that are fed from 'Main()' are checked to see what the user can bet)
Console.WriteLine("Here are your current points: "+points+"\nHow much would you like to bet?\nmin bet: 1\nmax bet: 10");
try
{
usrbet = Convert.ToInt32(Console.ReadLine());
}
catch
{
usrbet = 1;
}
winnings = slots(usrbet);
int RetWin = winnings - usrbet;
return RetWin;
Here's what the 'slots()' function does, with some examples of the winnings calculations
public static int slots(int usrbet)
{
int Win;
int x;
int y;
int z;
Random slot = new Random();
x = slot.Next(2,10);
y = slot.Next(2,10);
z = slot.Next(2,10);
Console.WriteLine(x+""+y+""+z);
Example 1
if(x == y && y == z && x== 7)
{
Win = usrbet*250;
Console.WriteLine("WOW! That's incredible, you just won "+Win+"!");
}
Example 2
else if(x == z)
{
Win = usrbet*5;
Console.WriteLine("Congratulations, you win "+Win+".");
}
Example 3
else
{
Win = 0;
Console.WriteLine("Ah, bummer. You didn't win anything this time.");
}
After that, the 'Win' value is sent back to 'game()', updating 'winnings'.
I apologize for poor formatting of the question. I'll include a link to the code, in case my problem lies elsewhere in the code: https://dotnetfiddle.net/D5TwL0
I've tried making arrays of the 'bet' and 'usrbet' variables, in an attempt to have a "new" value to update the 'points' with at every run of 'game()', but that changed absolutely nothing other than limiting how many times 'game()' can run before getting an overflow error.
It turns out that the problem wasn't with the code, but with the compiler. Dotnetfiddle is where I made the code and had the issue, but trying it in another compiler, it managed to count just fine.
(solution is at the end)
I have a tilemap with tiles that can spread to the adjacent tiles based on data I store in a dictionary by position.
While the check for that is done in no time at all it does take considerable resources resulting in a lag everytime I call the function.
Currently I'm making sure the code doesn't get executed too fast by actually waiting a little each time we finished a loop-iteration. While I'm okay with my code not being executed as fast as it could, this just isn't the right way to go about it.
So basically: Is there a way to limit the execution of a single script/function in unity/c# to not use more than a certain percentage of the games resources (or something to that effect)? Or maybe there's a way to increase the functions performance significantly I just can't find?
Thanks in advance!
Solution <-----------
Thanks to the great advice from Michael Urvan and akaBase I was able to vastly increase performance by using chunks. I wasn't able to implement all the suggested improvements, so check out (and upvote) their respective answers for even more performance.
How: I reset a bool every couple of seconds. When that bool is set, I loop through part of the necessary tiles (chunk) in the Update()-method and remember the last index for the next Update()-call. When I reach the end of the tile-list I'm done and can set the bool to false and reset the index.
Here's a simplified version of my finished code:
{
[SerializeField] private Tilemap tilemap;
[SerializeField] private List<TileType> tileTypes;
[SerializeField] private List<TileType> growthTileTypes;
private Dictionary<Vector3Int, TileData> tileDataByPosition;
private List<TileData> spreadingTileDatas;
private Dictionary<TileBase, TileType> tileTypeByTile;
private bool spreading;
private spreadIndex = 0;
private int chunkSize = 10;
private void Awake()
{
// set up stuff here
InvokeRepeating("ResetSpreading", 10, 10);
}
private void Update()
{
if (spreading == true)
{
Spread();
}
}
private void Spread()
{
for (
int index = spreadIndex;
index < spreadingTileDatas.Count && index < spreadIndex + chunkSize;
index++
)
{
// do stuff here
}
spreadIndex += chunkSize;
if (spreadIndex >= spreadingTileDatas.Count)
{
spreadIndex = 0;
spreading = false;
}
}
}
To answer your: "Is there a way to limit the execution of a single script/function in unity/c# to not use more than a certain percentage of the games resources (or something to that effect)?" - What you want to do is have this loop only perform a fraction of the total items per loop and call it more often. You can process it in chunks or you can use time measurement as I noted at the end of this post.
I didn't examine your code specifically to see exactly what is being done, but this is a common way of splitting up the processing over time rather than how you were adding a tiny WaitForSeconds() within a Coroutine. The coroutine with such a tiny WaitForSeconds is probably terrible for performance and simply using Update() (which is called every frame) and splitting processing the way I describe will yield much better performance. If you google Coroutine performance you can find information and benchmarks on that.
You will create a variable to keep track of the current position of your index, and a variable that is the maximum number of indexes to process per frame - you will break out of your loop when the total # of items processed hits the max. You also will not do the loop from 0 to X - it will be from currentIndex to the end and then loop back around by setting it to 0. You will just break out when you hit max each time.
To summarize:
Remove IEnumerator from Spread() and make it a regular function, and instead of foreach(position in tilePosition) use for(int i=0;) etc
Add a variable like MaxToProcessPerFrame
Add another variable like currentIndex
Add a third variable like totalProcessedThisFrame that is set to zero each time you enter Spread()
for instance if (totalProcessedThisFrame++ >= MaxToProcessPerFrame) break; in your loop
add if (currentIndex >= tilePositions.Count) currentIndex = 0 so that it loops back around to the beginning
I also noticed that Spread() is calling UpdateTiles() at the end, which then does another StartCoroutine(Spread()) which will cause it to run twice and i'm sure is inefficient in some way since it will kind of double process possibly per iteration.
A likely good place to call UpdateTiles() would be on the if (currentIndex >= tilePositions.Count) since that would be at the end of one iteration of updating the whole list.
Once you have that setup, you can test different values for the MaxToProcessPerFrame, start with a high number and then lower it until it seems to run smoothly and it will break up the processing over multiple frames.
A second method in addition to breaking it up by a total # of items to be processed is to use a System.Diagnostics Stopwatch() and break out of your loop when it exceeds a total amount of milliseconds of processing.
These two methods will help it process smoothly once you play with different amounts.
And lastly, your method of calling StartCoroutine() constantly is also not performant. You should start a coroutine once or the least number of times needed, and then loop within it. It's common to use a while (Application.isRunning) {} loop as a basis for most Coroutines in Unity.
also for reference:
Unity 2017 Game Optimization: Optimize all aspects of Unity performance
By Chris Dickinson
I suggest keeping the data in a Multidimensional Array and call the specific area of the tilemap you need.
Example pseudo code
TileData[,] tileMapDatas;
void Start()
{
int rows = 5;
int columns = 5;
tileMapDatas = new TileData[rows, columns];
for(int r = 0; r < rows, r++)
{
for(int c = 0; c < columns, c++)
{
tileMapDatas[r, c] = new TileData();//Change to how you create them
}
}
}
void Spread(Tile tile)
{
if(tile.CanSpread)
{
//Check the mapdata for the tile above, (add check for array bounds)
if(tileMapDatas[tile.Row + 1, tile.Column].CanChange)
{
//Change
}
//Repeat for other directions
}
}
Using this method is a lot more efficient as you only need to check the neighbours of the targeted tile.
i'm trying to make a clicker game in unity, where for every 100 "views" a dollar is generated, i'm not sure as to how to tackle this problem. i have tried using an update function that adds 1 to the dollar counter for every multiple of 100, but that misses out counting up if the number lands on say 101, i'm lost on what else to try as i'm a beginner really in this kind of code and i'm not really sure where to look for how to do this any other way.
One potential way to set this up would be to set a separate Limbo_Views variable alongside implied Total_Views and Total_Earnings variables to keep track of how far towards the next 100 view milestone the counter is or has surpassed
For example...
public class Video{
public int Total_Views;
public int Total_Earnings;
private int Limbo_Views;
public Video() {
Total_Views = 0;
Total_Earnings = 0;
Limbo_Views = 0;
}
public void addViews(int views) {
Total_Views += views; // <-- add views to total
Limbo_Views += views; // <- add views to limbo view count
if(Limbo_Views >= 100) { // <-- if there 100+ views in limbo...
int dollarsToAdd = Limbo_Views / 100; // <-- determine how many dollars should be paid out
Total_Earnings += dollarsToAdd; // <-- add that amount to earnings
Limbo_Views -= (dollarsToAdd * 100); // <-- decrement limbo views to reflect views that have been accounted for
}
}
}
This should be able to handle any positive number you throw at it in terms of adding views and accounting for their earnings.
Let me know if you have any additional questions!
I am doing some Codility coding practices and in the results, I got this "Detected time complexity: O(Y-X)". I do not understand what it actually means. It is because of my lousy and constant looping? How to enhance or improve the performance to get rid of this poor performance?
public static int Solution(int startPoint, int endPoint, int frogJumpPowerDistance)
{
int jumpCount = 0;
// AIM: return the number of jumps to reach endPoint
// CHECK: no jump needed.
if (startPoint == endPoint) return jumpCount;
int distanceReach = startPoint + frogJumpPowerDistance;
jumpCount++;
if (distanceReach < endPoint)
{
//enter checking loop
do
{
distanceReach += frogJumpPowerDistance;
jumpCount++;
}
while (distanceReach < endPoint);
}
return jumpCount;
}
I expect not to get a Timeout error. But I got it.
I am not sure how to improve my codes to solve the Timeout error.
For the input (5, 1000000000, 2) the solution exceeded the time limit.
For your information,
All inputs are within range [1 ... 1,000,000,000].
startPoint less than or equal to endPoint.
I think what "Detected time complexity: O(Y-X)" means is that it is saying your code takes longer to run when the start and end is farther apart. Specifically, the time it takes to run your code increases linearly with respect to the difference between start and end. Note that I am assuming Y is the end and X is the start.
You don't actually need a loop here. You can just do some maths to figure out how many jumps you need, and do this in constant time O(1).
First, you calculate the distance you have to jump by calculating the difference between start and end:
int distance = endPoint - startPoint;
Then, divide the distance by the jump power:
int jumps = distance / frogJumpPowerDistance;
If distance is divisible by frogJumpPowerDistance, then we can just return jumps. Otherwise, we still have some distance left, after we have jumped jumps times (this is integer division!). So we need one more jump to finish it off.
if (distance % frogJumpPowerDistance == 0) {
return jumps;
} else {
return jumps + 1;
}
EDIT:
As iakobski suggested in the comments, this can also be done with just one division, like so:
return (distance - 1) / frogJumpPowerDistance + 1;
I'm a fairly inexperienced programmer, and i'm currently working on a Console Application project. It's basically a little 'mathematics game'; the application generates two random numbers, that have either been added, subtracted, multiplied or divided against each other randomly. The answer is shown on screen and the user has to pick from the menu which is the right mathematical operator, once the correct answer is picked the application then displays on screen how long it took for the user in milliseconds to input the correct answer.
Now I want to save the times of the players in an array that can be called up later with all the scores. I need to include a method in this programme and I figured a method to save the times into an array would be suitable. I seem to have stumbled across a little problem though.
I'm not quite sure what's wrong:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Mathgame
{
class Program
{
}
class arrayclass
{
public static void saveInArray(int duration)
{
int[] TopTenScores = {000,1000,2000,3000,4000,5000,6000,7000,8000,9000};
if (duration < 1000)
{
duration = TopTenScores[000];
}
else if ((duration >= 1000) && (duration <= 1999))
{
duration = TopTenScores[1000];
}
else if ((duration >= 2000) && (duration <= 2999))
{
duration = TopTenScores[2000];
}
else if ((duration >= 3000) && (duration <= 3999))
{
duration = TopTenScores[3000];
}
else if ((duration >= 4000) && (duration <= 4999))
{
duration = TopTenScores[4000];
}
else if ((duration >= 5000) && (duration <= 5999))
{
duration = TopTenScores[5000];
}
else if ((duration >= 6000) && (duration <= 6999))
{
duration = TopTenScores[6000];
}
else if ((duration >= 7000) && (duration <= 7999))
{
duration = TopTenScores[7000];
}
else if ((duration >= 8000) && (duration <= 8999))
{
duration = TopTenScores[8000];
}
else if ((duration >= 9000) && (duration <= 9999))
{
duration = TopTenScores[9000];
}
Console.WriteLine(TopTenScores);
}
static void Main(string[] args)
{
int intInput, num1, num2, incorrect, array1;
float answer;
string input;
System.Random randNum = new System.Random();
Console.WriteLine("Welcome to the Maths game!");
Console.WriteLine("(Apologies for the glitchiness!)");
Console.WriteLine();
Console.WriteLine("Please choose from the following options:");
Console.WriteLine();
retry:
Console.WriteLine("1 - Test your Maths against the clock!");
Console.WriteLine("2 - Exit the application.");
Console.WriteLine("3 - Top scores");
Console.WriteLine();
input = Console.ReadLine();
intInput = int.Parse(input);
if (intInput == 1)
{
goto start;
}
else if (intInput == 2)
{
goto fin;
}
else if (intInput == 3)
{
array1 = array1.saveInArray;
goto retry;
}
Now, in the last 'else if' statement in the code, you can see my variable array1 trying to call the method, but no matter what I do I keep getting errors.
This is the only error I have at the moment, but I have a feeling soon as I resolve that error, another will come up. For now i'm just determined to get past this error:
'int' does not contain a definition for 'saveInArray' and no extension method 'saveInArray' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?).
Any help would be kindly appreciated, apologies in advanced for my ugly written code! And thank you to any help that I receive!
Regards,
Omar.
Okay,
There's quite a bit in this code that needs fixing TBH.
I'll start with your error first:
You are declaring the variable array1 as an integer. In C# integers are primitive types. This means that they have no methods and no class members. So, when you call array1.saveInArray the compiler is basically saying "the type integer doesn't have any methods... I can't find an appropriate method to match your call".
Instead of calling array1.saveInArray I think what you meant to call was arrayclass.saveInArray(x).
Notice the x in that call above. I'm passing a variable called x which is of type int into the function saveInArray().
This brings us to the second error. If saveInArray was a property, then you could just go arrayclass.saveInArray. However, it is a function which requires an argument... namely an integer.
When you call arrayclass.saveInArray(someInteger) you are passing someInteger as an argument into a method. The method is then free to use this argument to do its calculations.
That should fix your most basic errors and hopefully you can compile.
Moving on to some other errors that will cause you problems at runtime:
In the method saveInArray you are declaring an integer array called TopTenScores.
You are declaring this fine... however later on when you are indexing into TopTenScores, you are using an index that is way out of range of TopTenScores.
Here is your declaration of TopTenScores:
int[] TopTenScores = {000,1000,2000,3000,4000,5000,6000,7000,8000,9000};
Notice that there are 10 numbers in this declaration. This means that the max index you can have is 9. Why? Because arrays start indexing at 0 in C#.
I think you might be thinking that this array is associative... however this is not the case. When you do TopTenScores[1000] you are saying "give me the value at index 1000". This value does not exists and you will get a runtime error.
Instead, you would want to call TopTenScores[1] if you wanted to access the value 1000.
Also, you are not overwriting the default value with the new top score, rather you are overwriting the new top score with the default value. I don't think this is intended. Instead, switch your calls from this: duration = TopTenScores[1000];
to this: TopTenScores[1] = duration;
Edit: Lastly, as the commenter pointed out, using goto is bad practice and greatly discouraged. You will understand why later on as you start to understand program flow and organization better. For now, it is best to try and avoid the habit of using goto. goto maps to a low level system construct, which we should avoid when using a higher level language like C#. Your code can get confusing and error prone quickly with using goto.
Anyways, feel free to ask more questions/comment etc if you have questions or you need to clarify something. Welcome to StackOverflow!
One of your problems is that you're missing the fundamental concept of what an array is. Think of an array as a row of numbered mailboxes. This line of code:
int[] TopTenScores = {000,1000,2000,3000,4000,5000,6000,7000,8000,9000};
is creating an array of 10 integers that look something like this in memory:
Index Value
----- -----
0 0
1 1000
2 2000
3 3000
4 4000
5 5000
6 6000
7 7000
8 8000
9 9000
It is unclear how this is a useful structure to represent your top scores, and I'm not sure what your saveInArray method is trying to do. In that method, here's how one of your lines of code is interpreted:
duration = TopTenScores[1000];
What that means is "take what's at index 1000 of TopTenScores and store it in duration." As you can see from the table above, there is no index 1000, and besides, that code has nothing to do with saving a top score.
Another problem you're having is that you don't seem to have your algorithm in place for accomplishing the task. For the top ten functionality, try breaking down what needs to be done into instructions on how you would do it manually if you were a score keeper. It would be something like this:
I keep track of the top scores by stacking index cards with the scores on them in order of highest to lowest. At first, I have no index cards.
I can do two things: record scores and tell someone the top ten scores.
When someone asks me to record a score, I:
Write the score down on an index card
Look through the stack of previous scores in order until I find a score lower than this new score.
If I find a score that was lower than this new one, I place the card on top of the lower score.
Otherwise, if this score is the lowest one of the bunch, including if this is the first score recorded, I'll place the score at the bottom of the stack.
When someone asks me to tell them the top 10 scores, I:
Go through the first 10 cards or all the cards if I have less than 10.
For each of those scores, write them down in order.
Give the list of scores to the one requesting them.
It may seem silly to do this, but most programs are really only sequences of simple steps, and as a programmer, you need to be able to determine those steps before translating them into a language that the compiler can understand. Once you formulate the problem in simple terms, you can start translating it into code for example:
// You can think of a role a person would do for a manual process as a class
// in a program.
public class ScoreKeeper
{
// Our high score list (stack of cards) is empty to begin with. Unlike
// arrays, lists allow us insert items rather than placing them in
// numbered slots.
private List<int> _scores = new List<int>();
// This is the method for when someone asks us to record a score. The
// "score" parameter is the new score which you can think of as being
// written on a card.
public void RecordScore(int score)
{
// Go through each of the existing scores. "i" is the index in the
// list.
for (int i = 0; i < _scores.Count; i++)
{
// See if the new score is less than the score at index #i
if (_scores[i] < score)
{
// It is lower than this new score. Insert the new score
// above that score.
_scores.Insert(i, score);
// We're done. Stop looping and exit RecordScore.
return;
}
}
// If we get here, we found no scores lower than this new one. Add
// this score to the bottom of the stack.
_scores.Add(score);
}
// This is the method for when someone asks us for the top 10 scores.
// Notice that we return an array of integers, which will represent
// our piece of paper we hand back to the one requesting the scores.
public int[] GetTop10Scores()
{
// We start with a blank piece of paper.
int[] result = new int[10];
// Go through the scores. The first 10 are the top 10 because
// RecordScore puts them in order. We also need to make sure
// we don't try to get more scores than we've recorded.
for (int i = 0; i < 10 && i < _scores.Count; i++)
{
// Write down the score on the paper
result[i] = _scores[i];
}
// Send back the list of scores to the requester
return result;
}
}
Now, inside of your main program, you can create a ScoreKeeper and ask it to do its score keeping:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to the Maths game!");
Console.WriteLine("(Apologies for the glitchiness!)");
Console.WriteLine();
Console.WriteLine("Please choose from the following options:");
Console.WriteLine();
// This object keeps track of scores
ScoreKeeper scoreKeeper = new ScoreKeeper();
bool keepRunning = true;
while (keepRunning)
{
Console.WriteLine("1 - Test your Maths against the clock!");
Console.WriteLine("2 - Exit the application.");
Console.WriteLine("3 - Top scores");
Console.WriteLine();
string input = Console.ReadLine();
int intInput = int.Parse(input);
if (intInput == 1)
{
// You should avoid gotos. Try writing a method instead
// Play the game and get the player's score.
int newScore = PlayGame();
// Have the score keeper record the new score.
scoreKeeper.RecordScore(newScore);
}
else if (intInput == 2)
{
keepRunning = false;
}
else if (intInput == 3)
{
// Get the top scores from the score keeper
int[] topScores = scoreKeeper.GetTop10Scores();
// Print each score
for (int i = 0; i < topScores.Length; i++)
{
Console.WriteLine("{0}: {1}", i + 1, topScores[i]);
}
}
}
}
private static int PlayGame()
{
// Put your game logic in here. Return the score.
}
}
Once you become more familiar with the fundamentals of programming, you'll find there are existing classes you can reuse, like SortedList, that can already take care of common tasks like maintaining an ordered list.