I have 4 "mana balls". They are 4 individual game objects. After player casted a spell, one magic ball is spent and so on until he has spent all 4 magic balls. I want to develop a recharging system. When player casted a spell and first mana ball was spent, it needs to recharge again after 10 sec. If he casted again, second mana ball was spent and first cancels its recharging, and recharging of second mana ball began. After 10 seconds, second mana ball has recharged, and then begins next 10 seconds of first mana ball recharging.
I have code for spending mana balls:
private void ManaBall()
{
if(finishMagic == true && manaball4 == false && manaball3 == false)
{
manaBall4.SetActive(false); // Mana ball sprite is deactivated
manaball4 = true; // Boolean to confirm this mana ball was deactivated (for Ifs)
manaBallEmpty4.SetActive(true); // Empty space sprite instead of mana ball is activated
}
else if (finishMagic == true && manaball4 == true && manaball3 == false)
{
manaBall3.SetActive(false);
manaball3 = true;
manaBallEmpty3.SetActive(true);
}
}
I have reversed code for recharging mana balls:
private void ManaReload()
{
if (manaball4 == true && manaball3 == false)
{
manaBallEmpty4.SetActive(false);
manaball4 = false;
manaBall4.SetActive(true);
}
else if (manaball4 == true && manaball3 == true)
{
manaBallEmpty3.SetActive(false);
manaball3 = false;
manaBall3.SetActive(true);
}
I've tried creating coroutine which checks if any mana balls were spent after player casted a spell, and if it was, it started recharging it. But everytime I casted a spell, that function would start, giving shorter intervals than 10 seconds in between.
I need some advice where or how to put recharging function in code.
Coroutine I've tried:
IEnumerator FinishSpellAttack()
{
finishMagic = true;
ManaBall();
yield return new WaitForSeconds(1f);
finishMagic = false;
yield return new WaitForSeconds(10f);
ManaReload();
}
Perhaps you should have an array of the mana ball bools. If you use one, set the last true element to false. Then have one method that counts up and sets the first false element back to true.
I might do something like this :
bool[] manaBalls = { true, true, true, true };
bool usingCoroutine = false;
void UseMana() {
for(int i = manaBalls.Length - 1; i >= 0; i--) {
if(manaBalls[i]) {
manaBalls[i] = false;
if(!usingCoroutine) {
StartCoroutine("Timer")
}
break;
}
}
}
void ReplenishMana() {
for(int i = 0; i < manaBalls.Length; i++) {
if(!manaBalls[i]) {
manaBalls[i] = true;
if(i == manaBalls.Length - 1) {
StopCoroutine("Timer");
}
return;
}
}
}
You'll want to spice this up and add the timer in appropriately. This is psuedu ish code.
UseMana would go from the end of the array to the front. The first unused one it finds gets used. ReplenishMana does the oppisite. It goes from front to back finding the first false element and sets it back to true. Then it checks if that's the end of the array, if it is, stop the coroutine.
sometimes try Invoke() method for creating delays.
https://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
Related
I am creating this simple 3D game but I'm having an issue with player vs computer mode. I want to be able to play vs 1 to 3 bots and when it's my turn I want the game to wait for me to click, but when it's the bots turn I want to them to auto play. What really happens with my code is that everything goes to auto play. I know the issue is with Update function because Update is called once per frame but I don't know how to fix it. Can you help me? The idea is to use a tracker/counter like i = 0 first. When it's 0 it means my character will play so it should wait for my mouse to be pressed. When it's not 0 it will be bots turn. This is the section of the code. Function veprimetELojtareve is the same as the code inside if(!bota).
private int i = 0;
// Update is called once per frame
void Update()
{
if (!bota)
{
if (Input.GetMouseButtonDown(0)) // Left Click
{
if (hasLanded)
Reset();
RollDice();
}
if (rb.IsSleeping() && !hasLanded && thrown)
{
hasLanded = true;
rb.useGravity = false;
rb.isKinematic = true;
SideValueCheck();
}
else if (rb.IsSleeping() && hasLanded && diceValue == 0)
RollAgain();
}
else
{
if (i == 0)
veprimetELojtareve();
else
{
if (hasLanded)
Reset();
RollDice();
if (rb.IsSleeping() && !hasLanded && thrown)
{
hasLanded = true;
rb.useGravity = false;
rb.isKinematic = true;
SideValueCheck();
}
else if (rb.IsSleeping() && hasLanded && diceValue == 0)
RollAgain();
}
i = (i + 1) % numriLojtareve;
Debug.Log("Vlera e i eshte: " + i);
}
}
I am working on the walking sounds for my game, I am wanting it to play a sound and then wait and play the next. As it is walking I only want it to do this when w is pressed. I have been trying to use an Enumerator to do so but I am having issues where it will not start the function.
private IEnumerator audioPlayCoroutine;
public AudioSource[] steps;
public AudioSource[] cabinSteps;
private bool running;
void Start()
{
running = false;
audioPlayCoroutine = AudioPlay();
}
void Update()
{
if (Input.GetKey("w"))
{
if (running == false)
{
running = true;
}
}
if (running == true)
{
StartCoroutine(audioPlayCoroutine);
}
else if (running == false)
{
StopCoroutine(audioPlayCoroutine);
}
}
IEnumerator AudioPlay()
{
int ran = Random.Range(0, 4);
steps[ran].Play();
yield return new WaitForSeconds(2);
}
Okay, there are multiple reasons why it doesn't work:
For one running is never set to false. Once your player pressed w once, it will stay true.
Instead write
if (Input.GetKey("w"))
running = true;
else
running = false;
Additionally you start and stop your Coroutine multiple times.
Instead you should either check, whether the running status either changed or you should just check running in your coroutine.
It could then look like this:
void Start() {
running = false;
audioPlayCoroutine = AudioPlay();
StartCoroutine(audioPlayCoroutine);
}
void Update() {
if (Input.GetKey("w"))
running = true;
else
running = false;
}
IEnumerator AudioPlay()
{
while (true) {
if(!running)
yield return new WaitForSeconds(0);
int ran = Random.Range(0, 4);
steps[ran].Play();
// if you want it to play continuously, you can use the clip length for
// the sleep. This way it will start playing the next clip right after
// this one is done. If you don't want that, go with your code
yield return new WaitForSeconds(steps[ran].clip.length);
}
}
If your clip is actually 2 seconds long, you might also want to interrupt it from playing once your user stops pressing the button.
My friends I made some parts of this game but I have two problems in this game.
1)Problem about distinguishing ball collision with obstacles
1-1)A first problem is related to complex guidelines that I used them for simulation of ball collision with board or ball collision with blocks. These guidelines are not accurate especially when the ball meets with block corner or bottom.
If it is not better for collision distinguish I use a better code. I wrote this code. Are you think that are there better way?
2)How I can do a work that if the ball collision with any obstacle, the obstacle report the collision.
My aim is about using of events.
Are you thinking that I make obstacles as runtime?
If I make as a runtime, how I can make collision event for them?
With best regards
private Point MouseDownLocation;
int step = 2;
int stepleft = 2;
bool flagBottom;
bool flagTop;
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 10;
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
ball.Top += step;
ball.Left += stepleft;
//board simulate collision
bool collisonX = ball.Location.X + ball.Width > board.Location.X && ball.Location.X < board.Location.X + board.Width;
bool collisonY = ball.Top + ball.Height == board.Location.Y || ball.Top + ball.Height - 1 == board.Location.Y;
//board2(button1) simulate collision
bool collisonX2 = ball.Location.X + ball.Width > board2.Location.X && ball.Location.X < board2.Location.X + board2.Width;
bool collisonY2 = ball.Top + ball.Height == board2.Location.Y || ball.Top + ball.Height - 1 == board2.Location.Y;
//Collision the ball with under buttons
bool collsionButtonY = ball.Top - ball.Height == board2.Location.Y || ball.Top - ball.Height == board2.Location.Y - 1;
//collision leftwall
bool leftWall = ball.Left == 0 || ball.Left == -1 || ball.Left == 1;
//collision rightwall
bool topWall = ball.Top == 0 || ball.Top == -1 || ball.Top == 1;
bool bottomWall = collisonX && collisonY;
bool toppWall = collisonX2 && collisonY2;
//collision
bool barrier = collisonX2 && collsionButtonY;
//rightwall
bool rightWall = ball.Left + ball.Width == this.ClientSize.Width || ball.Left + ball.Width == this.ClientSize.Width - 1;
// sidewall = collision rightwall or leftwall
bool sideWall = leftWall || rightWall;
//Check the ball hit the ground
bool check = ball.Top + ball.Height < this.ClientSize.Height;
//if topWall true,This means that the ball is hit to the topwall
if (topWall)
{
flagBottom = false;
flagTop = true;
if (stepleft > 0)
{
step = 2;
}
else if (stepleft < 0)
{
step = 2;
}
}
//if bottomWall true,This means that the ball is hit to the board
else if (bottomWall)
{
flagBottom = true;
flagTop = false;
if (stepleft > 0)
{
step = step * -1;
}
else if (stepleft < 0)
{
step = step * -1;
}
}
//if barrier true and flagbottom true,This means that the ball is hit to the board2(button1)
else if (barrier && flagBottom)
{
if (stepleft > 0)
{
step = step * -1;
}
else if (stepleft < 0)
{
step = step * -1;
}
}
//if toppWall true and flagTop true,This means that the ball is hit to The top button is hit
else if (toppWall && flagTop)
{
if (stepleft > 0)
{
step = step * -1;
}
else if (stepleft < 0)
{
step = step * -1;
}
}
else if (sideWall)
{
//if leftwall true,This means that the ball is hit to the left side wall
if (leftWall)
{
if (flagTop)
{
stepleft = 2;
}
else if (flagBottom)
{
stepleft = 2;
}
}
//if rightWall true,This means that the ball is hit to the left side wall
else if (rightWall)
{
if (flagTop)
{
stepleft = -2;
}
else if (flagBottom)
{
stepleft = -2;
}
}
}
//check if ckeck==ture,this mean the ball is hit the ground
else if (!check)
{
timer1.Enabled = false;
}
}
private void board_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
MouseDownLocation = e.Location;
}
}
private void board_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
board.Left = e.X + board.Left - MouseDownLocation.X;
}
}
Define each "Wall" as an Object, this will increase your codes readablility. it will also allow you to define an Event for each wall, Listen to t. that way you know which wall has been hit
N.b. this is pseudo code. It wont compile :P
public class Wall
{
int X,Y,W,H;
//Define an event, add a listener to this, becuase we will fire this when there is a collision
public event EventHandler OnCollision;
public void CollisionCheck(Ball playerBall)
{
//1 Check for a collision with the "Ball" object
if(Ball.Rectangle().Intersects(this.Rectangle))
this.OnCollision(this, EventArgs.Empty); //Fire the event, null check might be requried
}
}
I do not know your game but (suspect 2D brick-like game)
Objects/Obstacles
all objects/obstacles can be simplified to rectangular objects
so make list of them with defined parameters like size and position
if they are movable/dropable then also speed and state...
type of object (affects color,scoring,capabilities...)
create member function like update(dt),draw()
create events you like OnHIt,OnCollision,...
for example (C++ pseudo code):
// game object types ...
enum _game_object_type_enum
{
_game_object_type_none=0,
_game_object_type_wall,
_game_object_type_brick1,
_game_object_type_brick2,
_game_object_type_brick3,
_game_object_type_bomb,
_game_object_type_joker1,
_game_object_type_joker2,
...
};
// game object class
class game_object
{
public;
int type; // type of object
float x,y,vx,vy,hx,hy; // position,speed,half size of rectangle
void update(float dt); // this call periodicaly in some timer updates positions,... and call events, dt is time passed
void draw(); // can add some rendering device context ...
};
// whole game class holds the game world/board/map and player(s) ...
class game_board
{
public;
game_object *go; int gos; // list of all objects in game
void update(float dt) { for (int i=0;i<gos;i++) go[i].update(dt); }
void draw() { for (int i=0;i<gos;i++) go[i].draw(); }
}
events and flow control
first define some events you need
either you use global function/pointers for that or as members
depends on your platform and to what you are used to
for example like this:
void OnCollisionFX(game_object o1,game_object o2)
{
// do something ...
}
void (*OnCollision)(game_object o1,game_object o2)=OnCollisionFX;
for player interactions only (like player controlled object collisions test only)
is better to handle this in game_object::update(dt) but you need to add player context
something like game_object::update(dt,plr)
where plr is pointer to player controlled object or player class
if you have massive interactivity between game objects (like brick/brick collisions)
then is better to handle this in game_board::update(dt)
because otherwise you will need to provide references for everything into update call
and that will quickly lead to heap/stack trashing and very slow performance
also more advanced multiple object collision/interaction approaches are better in main game class
here is an example for first case:
void game_object::update(float dt,game_object &plr)
{
// update position
x+=vx*dt;
y+=vy*dt;
// here you should make your own collision test of coarse
if (fabs(x-plr.x)<=hx+plr.hx)
if (fabs(y-plr.y)<=hy+plr.hy)
{
... do what you need like mirror or stop speed or clamp position ...
if (OnCollision) OnCollision(this,plr);
}
}
in second case you should check each object against each other
void game_board::update(float dt)
{
int i,j;
// update position
for (i=0;i<gos;i++)
{
go[i].x+=go[i].vx*dt;
go[i].y+=go[i].vy*dt;
}
// here you should make your own collision test of coarse
for (i=0;i<gos;i++)
for (j=i+1;j<gos;j++) // the lower indexes are already tested
if (fabs(go[i].x-go[j].x)<=go[i].hx+go[j].hx)
if (fabs(go[i].y-go[j].y)<=go[i].hy+go[j].hy)
{
... do what you need like mirror or stop speed or clamp position ...
if (OnCollision) OnCollision(go[i],go[j]);
}
}
performance
you can improve performance by use of type property
for example if some game objets are static then the do not need to update position
if some are indestructible then there can be also some processing avoided and so on ...
[notes]
dt/fps/speed should be combined
so in each frame your player controlled object or any moving object does not move further
then the half size of smallest object
if this is not true then you need better collision test
use line intersection algorithm
last position and actual position gives you one line
and object edge gives you the other one
if booth objects are moving then that is even more complicated :)
I have the following method, called in my update loop, to check if one of two buttons has been pressed.
private void CheckInputs()
{
if (CheckButton(startNewGameButtonTexture, startNewGameButtonPosition))
{
//break 1 is here
}
if (CheckButton(continueCareerButtonTexture, continueCareerButtonPosition))
{
//break 2 is here
}
}
Buttons are just rectangles, and if the mouse is over them, and the click is released the CheckButton bool returns true:
public bool CheckButton(Texture2D texture, Vector2 vector)
{
MouseState CurrentMouseState = Mouse.GetState();
bool outcome;
if (CurrentMouseState.X > vector.X && CurrentMouseState.X < vector.X + texture.Width &&
CurrentMouseState.Y > vector.Y && CurrentMouseState.Y < vector.Y + texture.Height)
{
if (CurrentMouseState.LeftButton == ButtonState.Released && PreviousMouseState == ButtonState.Pressed)
{
outcome = true;
}
else
{
outcome = false;
}
}
else
{
outcome = false;
}
PreviousMouseState = CurrentMouseState.LeftButton;
return outcome;
}
In the current order, the startNewGameButton works (ie debugging stops at break 1), but the continueCareerButton doesn't work (clicking on the button does not trigger break 2).
But if I change the order of the the checks to this:
private void CheckInputs()
{
if (CheckButton(continueCareerButtonTexture, continueCareerButtonPosition))
{
//break 2 is here
}
if (CheckButton(startNewGameButtonTexture, startNewGameButtonPosition))
{
//break 1 is here
}
}
the continueCareerButton now works (break 2), but the startNewGameButton now doesn't (break 1).
I think the mouse states must not be working properly, but I can't work out why.
You are setting your PreviousMouseState every time you check.
You should be setting it once per frame, the simplest way to do this is inside the Update() function:
void Update(..) {
CurrentMouseState := Mouse.GetState()
// code...
PreviousMouseState = CurrentMouseState
}
Otherwise it will only ever work for the first call of your CheckButton() function instead of every call.
As soon as the button is released, you are setting PreviousMouseState to ButtonState.Released in the first call to CheckButton. Thus the second call to CheckButton can never return true (because PreviousMouseState is almost always equal to CurrentMouseState.LeftButton). Don't set PreviousMouseState until after both if statements.
I am programming a penalty shoout game in xna, my question is how do I count just 1 goal and then let time for a goal message??,
I already solve the collison between the goalkeeper, goal and the goal area, and I keep and score but when the ball touched the goal area, the score always increment when the ball touched the goal area, how do I just count 1 goal and then display a goal message??
this is what I have so far, I think about a delay but its not working, please help I am stucked in this
if (_gameState == GameState.Playing)
{
if (CollideGoalArea())
{
if (CollideGoalKeeper())
{
_messageToDisplay = "Goalie defends the goal!";
this.Window.Title = "Missed!";
_gameState = GameState.Message;
}
else
{
score++;
_messageToDisplay = "GOAL!";
this.Window.Title = "Goal!";
_gameState = GameState.Message;
}
}
else
{
_messageToDisplay = "You missed the goal.";
_gameState = GameState.Message;
}
}
else if (_gameState == GameState.Message)
{
if (Mouse.GetState().RightButton == ButtonState.Pressed)
{ // right click to continue playing
_gameState = GameState.Playing;
balonpos.X = 300;
balonpos.Y = 350;
}
}
The problem is happening because your code increments score any time it detects that ball is inside the goal. Since (by default) Update is called 60 times per second, if your ball is inside the goal score will get incremented by 60 every second.
You could rewrite your code so that your game has 2 states: Displaying a message state and playing state:
enum GameState
{
Playing,
Message
}
GameState _gameState = GameState.Playing;
String _messageToDisplay = "";
int _score = 0;
protected override void Update(GameTime gameTime)
{
if(_gameState == GameState.Playing)
{
if(CollideGoalArea())
{
if(CollideGoalkeeper())
{
_messageToDisplay = "Goalie defends the goal!";
_gameState = GameState.Message;
}
else
{
_messageToDisplay = "GOAL!";
score++;
_gameState = GameState.Message;
}
}
else
{
_messageToDisplay = "You missed the goal.";
_gameState = GameState.Message;
}
}
else if(_gameState == GameState.Message)
{
if(Mouse.GetState().RightButton == ButtonState.Pressed) // right click to continue playing
_gameState = GameState.Playing;
//you should also reset your ball position here
}
}
This way, whenever ball enters the goal area, it will either be a score, miss or hit on the goalkeeper. The game will then instantly change state so that those conditions aren't checked again until you right click with your mouse.
You could also put your input detection logic and ball and goalie position update logic inside these if's (you might not want for the player to be able to shoot another ball while the message is being displayed).