I'm trying to add a shield that is active for a limited to my game but right now it doesn't work for some reason and I just can't figure out why.
First, this is the shield class:
class Shield : SpriteGameObject
{
protected float counter;
protected float limit;
public bool isShieldOn;
protected bool startCounting;
public Shield(int layer = 0, string id = "") : base("Sprites/spr_shield", layer, id)
{
counter = 0;
limit = 5;
isShieldOn = false;
startCounting = false;
}
public override void Update(GameTime gameTime)
{
//Stuff here
while (startCounting)
counter += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (counter >= limit)
{
isShieldOn = false;
counter = 0;
startCounting = false;
}
Player player = GameWorld.Find("player") as Player;
if (this.visible && this.CollidesWith(player))
{
isShieldOn = true;
startCounting = true;
this.visible = false; GameEnvironment.AssetManager.PlaySound("Sounds/snd_shieldcollected");
}
}
}
Right now the game freezes when the player touches the shield but that has something to do with the while statement that contains the counter. I don't know why but first let me explain the isShieldOn bool I've been having problems with.
Here is where is use the isShieldOn bool:
class Health : GameObjectList
{
Shield shield;
public Health(int layer = 3, string id = "") : base(layer, id)
{
//Stuff here
shield = new Shield(1, "shields");
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
if (shield.isShieldOn == false)
{
//Remove health
}
}
}
At the moment the shield works when I just give it the value true in the constructor. But it doesn't become true if I try to assign it the if statement in the Update method of the Shield class. I'm 100% sure the if statement is performed because the shield does disappear when touched (this.visible = false) and the soundeffect plays.
If you have any ideas on how to fix this problem I'd be really thankfull, also if you have an idea on how to fix the timer.
Replace
while (startCounting)
counter += (float)gameTime.ElapsedGameTime.TotalSeconds;
to
counter += (float)gameTime.ElapsedGameTime.TotalSeconds;
in your Update method().
Related
So I am making a VR game and what I want to do is, as soon as I press the trigger on the joystick the "sword" gets activated and can stay activated for 1 second, after that it has a cooldown of 1 second also in which it cannot get activated, and then it resets. I thought it would be simple but I can't for the life of me make it work.
Here's the code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR;
public class Sword : MonoBehaviour
{
public SteamVR_Action_Boolean ActivateSword;
public SteamVR_Input_Sources handType;
private bool IsSwordActivated = false;
private bool canSwordGetActivated = true;
private bool cooldownStart = false;
public Material activatedSword;
public Material defaultSword;
public float timeStamp;
public float timer = 0;
public float cooldown = 2;
void Start()
{
ActivateSword.AddOnStateDownListener(TriggerDown, handType);
ActivateSword.AddOnStateUpListener(TriggerUp, handType);
timeStamp = Time.time;
}
public void TriggerUp(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
Debug.Log("Trigger is up");
IsSwordActivated = false;
this.GetComponent<MeshRenderer>().material = defaultSword;
}
public void TriggerDown(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
if (canSwordGetActivated == true)
{
Debug.Log("Trigger is down");
IsSwordActivated = true;
cooldownStart = true;
this.GetComponent<MeshRenderer>().material = activatedSword;
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
if (IsSwordActivated == true)
{
Destroy(collision.gameObject);
}
}
}
private void Update()
{
//if (timeStamp <= Time.time)
//{
// if (IsSwordActivated == true)
// {
// timeStamp += 2;
// canSwordGetActivated = false;
// Debug.Log("test");
// }
//}
if (cooldownStart == true)
{
timer += Time.deltaTime;
cooldown -= Time.deltaTime;
if (timer >= 1f)
{
this.GetComponent<MeshRenderer>().material = defaultSword;
IsSwordActivated = false;
timer = 0;
}
if (timer == 0)
{
canSwordGetActivated = false;
}
if (cooldown <= 1f)
{
canSwordGetActivated = false;
}
if (cooldown <= 0)
{
cooldown = 2f;
canSwordGetActivated = true;
cooldownStart = false;
}
}
}
}
You should not use Update but a Coroutine for that. Especially the "cooldown" should run independently from the "timer".
// Not needed
//private bool cooldownStart = false;
//public float timer = 0;
// reference this already via the Inspector if possible
[SerializeField] private MeshRenderer _meshRenderer;
[SerializeField] private float cooldownTime = 1;
[SerializeField] private float maxEnabledTime = 1;
// Otherwise get it only ONCE on runtime
private void Awake()
{
if(!_meshRenderer) _meshRenderer = GetComponent<MeshRenderer>();
}
public void TriggerUp(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
Debug.Log("Trigger is up");
// stop the timeout
StopAllCoroutines();
// disable sword and start cooldown
StartCoroutine(SwordCooldown());
}
public void TriggerDown(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
if(IsSwordActivated) return;
if (!canSwordGetActivated) return;
Debug.Log("Trigger is down");
// start timeout
StartCoroutine(SwordEnabledTimer());
}
///<summary>
/// Disables sword and Runs cooldown before sword can be enabled again
///<summary>
private IEnumerator SwordCooldown()
{
canSwordGetActivated = false;
IsSwordActivated = false;
_meshRenderer.material = defaultSword;
yield return new WaitForSeconds(cooldownTime);
canSwordGetActivated = true;
}
///<summary>
/// Disables the sword and jumps to cooldown after it was enabled for too long
///<summary>
private IEnumerator SwordEnabledTimer()
{
canSwordGetActivated = false;
yield return new WaitForSeconds(maxEnabledTime);
StartCoroutine(SwordCooldown());
}
See WaitForSeconds
Sideeffect/Advantage:
Coroutines are often better read- and maintainable and more flexible then polling states in Update!
For example You can now very simple extend both routines and add a progress display for both like e.g.
private IEnumerator SwordCooldown()
{
canSwordGetActivated = false;
IsSwordActivated = false;
_meshRenderer.material = defaultSword;
var timePassed = 0f;
while(timePassed < cooldownTime)
{
var cooldownProgress = timePassed / cooldownTime;
// do something with the cooldownProgress value 0-1
timePassed += Mathf.Min(Time.deltaTime, cooldownTime - timePassed);
yield return null;
}
canSwordGetActivated = true;
}
One simple way of doing this would be a coroutine:
class Sword {
private bool isCoolingDown;
public void Swing() {
if (isCoolingDown) {
Debug.Log("Sorry, cooling down right now");
return;
}
StartCoroutine(DoSwingSword());
}
private IEnumerator DoSwingSword() {
isCoolingDown = true;
yield return new WaitForSeconds(1);
isCoolingDown = false;
}
}
This will use a bool to track if you are in cooled down state and automatically reset this bool after one second, without you having to track Time.deltaTime. You can then wrap the rest of your logic around this bool.
I'm trying to add a health bar to my 2d platformer. Atm i'm trying to define a collision between two objects (a rocket and the player), but I don't know how to refer/use them. I've written the error as a comment in the code.
This is the health class:
using Microsoft.Xna.Framework;
class Health : GameObjectList
{
public Health(int layer = 3, string id = "") : base(layer, id)
{
for(int i = 0; i < 255; i+=85)
if(i % 85 == 0)
{
SpriteGameObject health = new SpriteGameObject("Sprites/spr_health", 3);
health.Position = new Vector2(120 + i, 15);
this.Add(health);
}
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
Player player = GameWorld.Find("player") as Player;
Rocket rocket = GameWorld.Find("rocket") as Rocket;
foreach (GameObject obj in gameObjects)
{
SpriteGameObject h = obj as SpriteGameObject;
if (rocket.CollidesWith(player))
//This is wat it says as an error:
//An unhandled exception of type 'System.NullReferenceException' occurred in TickTick.exe
//Additional information: Object reference not set to an instance of an object.
{
this.Remove(h);
return;
}
}
}
}
I've used the same GameWorld.find method in the rocket class when there was no health bar and that worked:
public void CheckPlayerCollision()
{
Player player = GameWorld.Find("player") as Player;
if (this.CollidesWith(player) && this.Visible)
player.Die(false);
// CURRENTLY DON'T WANT THIS TO HAPPEN BECAUSE OF HEALTH BAR
}
This is the GameWorld Find method:
public GameObject Find(string id)
{
foreach (GameObject obj in gameObjects)
{
if (obj.ID == id)
return obj;
if (obj is GameObjectList)
{
GameObjectList objlist = obj as GameObjectList;
GameObject subobj = objlist.Find(id);
if (subobj != null)
return subobj;
}
}
return null;
}
The Rocket class:
using Microsoft.Xna.Framework;
class Rocket : AnimatedGameObject
{
protected double spawnTime;
protected Vector2 startPosition;
public Rocket(bool moveToLeft, Vector2 startPosition)
{
this.LoadAnimation("Sprites/Rocket/spr_rocket#3", "default", true, 0.2f);
this.PlayAnimation("default");
this.Mirror = moveToLeft;
this.startPosition = startPosition;
Reset();
}
public override void Reset()
{
this.Visible = false;
this.position = startPosition;
this.velocity = Vector2.Zero;
this.spawnTime = GameEnvironment.Random.NextDouble() * 5;
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
if (spawnTime > 0)
{
spawnTime -= gameTime.ElapsedGameTime.TotalSeconds;
return;
}
this.Visible = true;
this.velocity.X = 600;
if (Mirror)
this.velocity.X *= -1f;
CheckPlayerCollision();
// check if we are outside the screen
Rectangle screenBox = new Rectangle(0, 0, GameEnvironment.Screen.X, GameEnvironment.Screen.Y);
if (!screenBox.Intersects(this.BoundingBox))
this.Reset();
}
public void CheckPlayerCollision()
{
Player player = GameWorld.Find("player") as Player;
if (this.CollidesWith(player) && this.Visible)
player.Die(false);
// CURRENTLY DON'T WANT THIS TO HAPPEN BECAUSE OF HEALTH BAR
}
}
I just have no idea how i can get this to work, to decrease the health by 1 and eventually let the player die when there is no health left.
it seems to me that one or both the lines
Player player = GameWorld.Find("player") as Player;
Rocket rocket = GameWorld.Find("rocket") as Rocket;
returns null, do that check, ensure that the gameObjects list cointains what you are looking for and that they are of the type you expect
check even if when your player Die, you remove it from the world before the code part where it gives you errors, maybe at the time of your check the player (or the rocket) has been removed from the world
I am creating a 2D game and I've hit a serious road block when trying to 'randomly' spawn power ups. Basically what I am attempting to do is spawn the power ups off screen and then they move onto the screen (at the same speed as my scrolling background) Once one of the three power ups has been spawned another one won't spawn until 10 to 30 seconds has passed. I also understand that the shield power Up is the only one that I am trying to get spawning randomly at the moment. I have been researching for hours and writing code that I will post below. I have tried to do this all by myself so I apologize for the quality of the code, I am still a novice and learning. Any help or links to websites would be greatly appreciated as I have no idea where to proceed from here.
Thanks in advance.
game1.cs
public List<PowerUps> powerUpList = new List<PowerUps>();
public double counterPower = 0;
public bool powerUpCollision = false;
public bool invincibility = false;
Sprite shieldPower;
Sprite infiniteAmmoPower;
Sprite livesPower;
bool isVisible = true;
public bool infiniteAmmoBool = false;
public bool infiniteAmmoCol = false;
protected override void Initialize()
{
shieldPower = new Sprite();
infiniteAmmoPower = new Sprite();
livesPower = new Sprite();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
shieldPower.LoadContent(this.Content, "powerUpShield");
infiniteAmmoPower.LoadContent(this.Content, "infinitePowerUp");
livesPower.LoadContent(this.Content, "livePowerUp");
}
protected override void Update(GameTime theGameTime)
{
}
public void powerUps(GameTime theGameTime)
{
//if (mPlayer.boundingBox.Intersects(shieldPower.boundingBoxShieldPower))
{
Console.WriteLine(shieldPower.Position.X);
if (shieldPower.isVisible == true)
{
Console.WriteLine("collision working");
powerUpCollision = true;
invincibility = true;
shieldPower.isVisible = false;
if (powerUpCollision == true && invincibility == true)
{
lives = lives - 0;
}
counterPower = 0;
}
}
counterPower += theGameTime.ElapsedGameTime.TotalSeconds;
{
// Console.WriteLine(counterPower);
if (counterPower > 7)
{
powerUpCollision = false;
invincibility = false;
}
}
if (mPlayer.boundingBox.Intersects(livesPower.boundingBoxLives))
{
if (livesPower.isVisible == true)
{
lives = lives + 1;
livesPower.isVisible = false;
}
}
}
public void loadPowerUps()
{
int randPowerX = random.Next(850, 1400); // spawns randomly between these coordinates
int randPowerY = random.Next(-300, 300);
Console.WriteLine(shieldPower.Position.X);
if (powerUpList.Count < 1) // limits the powerUp count to 1 at a time
{
powerUpList.Add(new PowerUps(Content.Load<Texture2D>("powerUpShield"), new Vector2(randPowerX, randPowerY)));
}
for (int i = 0; i < powerUpList.Count; i++)
{
if (powerUpList[i].isVisible == false)
{
powerUpList.RemoveAt(i);
i--;
}
}
}
protected override void Draw(GameTime theGameTime)
{
foreach (PowerUps powerup in powerUpList)
{
powerup.Draw(spriteBatch);
}
mPlayer.Draw(this.spriteBatch);
enemyShip.Draw(this.spriteBatch);
if (shieldPower.isVisible == true)
{
shieldPower.Draw(this.spriteBatch);
}
if (livesPower.isVisible == true)
{
livesPower.Draw(this.spriteBatch);
}
if (infiniteAmmoPower.isVisible == true)
{
infiniteAmmoPower.Draw(this.spriteBatch);
}
}
PowerUps.cs
public class PowerUps : Game1
{
Random randomPower = new Random();
int minPowerSpawnTimer = 10000; // 10 seconds
int maxPowerSpawnTimer = 30000; // 30 seconds
double nextSpawnTime;
float powerUpSpeed = -0.5f;
Vector2 position;
Texture2D texture;
public PowerUps(Texture2D newText, Vector2 newPos)
{
position = newPos;
texture = newText;
}
public void Update(GameTime theGameTime)
{
position.X += powerUpSpeed;
nextSpawnTime -= theGameTime.ElapsedGameTime.Milliseconds;
if (nextSpawnTime < 0)
{
loadPowerUps();
resetSpawnTime();
}
}
public void resetSpawnTime()
{
nextSpawnTime = randomPower.Next(minPowerSpawnTimer, maxPowerSpawnTimer);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.White);
}
}
Your problem is the class structure you've created. Your should not be inheriting Game1 in your PowerUps class. It is creating multiple instances of Game1 objects each of which has its own list of PowerUps.
It doesn't appear to me that any of the Update() methods in the PowerUps instances will ever be called.
I think you may be wanting to access functionallity of your Game1 class from your PowerUps class. Inheriting (PowerUps : Game1) is not the way to do it here.
Instead, make a PowerUps class that does not inherit anything. Then from your Game1 Update method, call your PowerUps instances' Update method and pass what variables it needs. Same with the drawing.
In order for your Update method to be called, your PowerUps class must inherit from GameComponent, not Game1.
Since you also have a Draw method, to have your Draw method also called automatically your must inherit from DrawableGameComponent instead.
Those base classes take your Game1 as a parameter, so you will have to modify your constructor:
public class PowerUps : DrawableGameComponent
{
//Variables
public PowerUps(Texture2D newText, Vector2 newPos, Game1 game) : base(game)
{
position = newPos;
texture = newText;
}
Now that you have a GameComponent, you must register it in your Game1 class upon creation. So, when creating your power ups in your powerUpList, change your code from
powerUpList.Add(new PowerUps(Content.Load<Texture2D>("powerUpShield"), new Vector2(randPowerX, randPowerY)));
to:
PowerUps powerUp = new PowerUps(Content.Load<Texture2D>("powerUpShield"), new Vector2(randPowerX, randPowerY))
powerUpList.Add(powerUp);
Components.Add(powerUp);
When your component is registered within the Components list, the Game can start calling the Draw and Update methods of your component. Once you're finish with it, remove it from the list.
i already make the player character to move to it is destination (animate the character to move to that destination), i wanted to disable the GUI button when the player character is moving, but i can't get it and work it out. Could you guys help me?
Here is the code:
public class UserPlayer : Player
{
public override void TurnUpdate()
{
//Moving animation
if (positionQueue.Count > 0)
{
GUI.enabled = false; // This is the one that i want when the character still moving (animation still moving), disabled the GUI. But i can't get it
transform.position += (positionQueue[0] - transform.position).normalized * moveSpeed * Time.deltaTime;
if (Vector3.Distance(positionQueue[0], transform.position) <= 0.1f)
{
transform.position = positionQueue[0];
positionQueue.RemoveAt(0);
if (positionQueue.Count == 0)
{
actionPoints--;
}
}
}
base.TurnUpdate();
}
public override void TurnOnGUI()
{
base.TurnOnGUI();
}
}
public class Player : MonoBehaviour
{
//for movement animation
public List<Vector3> positionQueue = new List<Vector3>();
public virtual void TurnUpdate()
{
if (actionPoints <= 0)
{
actionPoints = 2;
magicAttacking = false;
moving = false;
attacking = false;
GameManager.instance.NextTurn();
}
}
public virtual void TurnOnGUI()
{
if (GameManager.instance.currentPlayerIndex == 0 || GameManager.instance.currentPlayerIndex == 2)
{
//ToolTip Text
move = GUI.Button(buttonRect, new GUIContent("Move", "Move the Player"));
GUI.Label(tooltipRect, GUI.tooltip, label1);
GUI.tooltip = null;
//Move Button
if (move)
{
if (!moving)
{
GameManager.instance.RemoveTileHighlights();
moving = true;
attacking = false;
GameManager.instance.HighlightTilesAt(gridPosition, Color.blue, movementPerActionPoint);
}
else
{
moving = false;
attacking = false;
GameManager.instance.RemoveTileHighlights();
}
}
}
Your question has been already made, take a look here.
Adapt your code in this way:
public virtual void TurnOnGUI() {
if (GameManager.instance.currentPlayerIndex == 0 || GameManager.instance.currentPlayerIndex == 2) {
...
if(!moving) {
move = GUI.Button(buttonRect, new GUIContent("Move", "Move the Player"));
GUI.Label(tooltipRect, GUI.tooltip, label1);
}
...
}
}
Im making a simple game with Microsoft XNA and I have an Enemy.cs class which consists of a property enemyMoveSpeed.
public class Enemy
{
float enemyMoveSpeed { get; set }
}
In the Game1.cs in update section I have put a timer which would constantly change the following property.
protected override void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
timer -= elapsed;
if (timer < 0)
{
foreach (Enemy E in enemies)
{
E.enemyMoveSpeed += 1;
}
timer = TIMER; //Reset Timer
}
}
Every new instance of Enemy would be affected after the change. Can somebody give me an idea how to implement this?
Create a List
List<Enemy> MyEnemyList;
//of course you will have to add Enemies to this list somehow. Probably you already have a list or an array, wich behaves the same for this purpose.
foreach (Enemy E in MyEnemyList)
{
E.enemyMoveSpeed += 1; //if you are incrementing the speed of everyone by 1.
//or any other option you think is better
}
Use a static property for the new speed, and in your Enemy constructor you can set the instance property to match this.
Something like
class Enemy
{
public static int NewEnemySpeed { get; set;}
public int Speed {get; set;}
public Enemy()
{
Speed = NewEnemySpeed;
}
}
From your Game1.Update, you can do this
Enemy.NewEnemySpeed = ..//some new speed
Maybe something like this will help you.
public class Enemy
{
public Enemy()
{
EnemyState = new EnemyState { IsDirty = true, Position = Vector2.Zero };
Speed = 3.0f;
}
public EnemyState State { get; set }
public float Speed { get; set; }
}
public class EnemyState
{
public bool IsDirty { get; set }
public Vector2 Position { get; set; }
}
protected override void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
// If should spawn enemy
// var enemy = new Enemy();
// enemy.State.Position = // TODO insert starting position
// enemies.Add(enemy);
// Find all enemies that should be moved
// Set IsDirty = true;
// Move each enemy that IsDirty
timer -= elapsed;
if (timer < 0)
{
foreach (Enemy enemy in enemies.Where(e => e.State.IsDirty))
{
enemy.State.Position.X += enemy.Speed;
enemy.State.IsDirty = false;
}
timer = TIMER; //Reset Timer
}
}