Changing class properties for all instances - c#

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
}
}

Related

How can I loop using foreach each time once in the Update and also using a bool flag each time once?

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Cinemachine;
public class Waypoints : MonoBehaviour
{
[Header("Objects To Move")]
public Transform objectToMovePrefab;
public int numberOfObjectsToMove = 1;
[Header("Speed")]
public float speed;
public bool randomSpeed = false;
public float minRandomSpeed = 1;
public float maxRandomSpeed = 100;
private bool changeSpeedOnce = false;
private bool currentSpeedState;
[Header("Waypoints")]
[SerializeField] private List<Transform> waypoints;
[Header("Delay")]
public bool useDelay = false;
public float delay = 3;
public bool randomDelay = false;
public float minRandomDelay = 0.3f;
public float maxRandomDelay = 5;
[Header("LineRenderer")]
public LineRenderer lineRenderer;
public bool moveOnLineRenderer = false;
public List<Vector3> lineRendererPositions = new List<Vector3>();
[Header("Cinemachine Cameras")]
public CinemachineVirtualCamera virtualCamera;
private List<WaypointsFollower> waypointsFollowers = new List<WaypointsFollower>();
private void Start()
{
currentSpeedState = changeSpeedOnce;
for (int i = 0; i < numberOfObjectsToMove; i++)
{
var parent = GameObject.Find("Moving Object Parent");
var objectToMove = Instantiate(objectToMovePrefab, parent.transform);
objectToMove.name = "Platfrom";
waypointsFollowers.Add(objectToMove.GetComponent<WaypointsFollower>());
}
virtualCamera.Follow = waypointsFollowers[0].gameObject.transform;
virtualCamera.LookAt = waypointsFollowers[0].gameObject.transform;
foreach (Transform wp in waypoints)
{
lineRendererPositions.Add(wp.position);
}
if (moveOnLineRenderer)
{
foreach (WaypointsFollower wpf in waypointsFollowers)
{
wpf.go = true;
}
}
SpeedUpdater();
if (useDelay)
StartCoroutine(SendObjectstomoveWithDelay());
}
private void Update()
{
lineRendererPositions.Clear();
lineRendererPositions.AddRange(GetLinePointsInWorldSpace());
SpeedUpdater();
}
public int Count => waypoints.Count;
public Vector3 GetWaypoint(int index)
{
return waypoints[index].position;
}
public int CountLR => lineRendererPositions.Count;
public Vector3 GetLRWaypoint(int index)
{
return lineRendererPositions[index];
}
IEnumerator SendObjectstomoveWithDelay()
{
{
foreach (WaypointsFollower follower in waypointsFollowers)
{
if (randomDelay)
{
delay = Random.Range(minRandomDelay, maxRandomDelay);
}
yield return new WaitForSeconds(delay);
follower.go = true;
}
}
}
private void SpeedUpdater()
{
foreach (WaypointsFollower follower in waypointsFollowers)
{
if (randomSpeed)
{
follower.speed = Random.Range(minRandomSpeed, maxRandomSpeed);
}
else
{
follower.speed = speed;
}
}
}
Vector3[] GetLinePointsInWorldSpace()
{
var positions = new Vector3[lineRenderer.positionCount];
//Get the positions which are shown in the inspector
lineRenderer.GetPositions(positions);
//the points returned are in world space
return positions;
}
}
Inside the SpeedUpdtaer I'm using a flag and I want to be able to change the flag in run time too. The problem is calling the SpeedUpdater in the Update will make the foreach loop every frame and it might be expensive because in the List waypointsFollowers there might be a lot of items.
and about the delay part, I want to do that if I change the useDelay flag in run time then from that moment each moving object will get to the next waypoint and then from there the delay will start working. same if changing the useDelay flag to false at run time then don't wait at the next waypoint/s.
The delay is working fine but only in the Start for now and also the SpeedUpdater is working fine but I'm not sure if making foreach each frame is a good way. and not sure how to make and wherein the code that I will be able to change the speed and the speed random flag at run time without making the loop each frame.
I would use properties. With properties you can keep track in the variable value set, to conditionally or on value change execute some logic, like this (I did not compile check);
private bool useDelay = false;
public bool UseDelay { get => useDelay; set {
useDelay = value;
if (useDelay)
StartCoroutine(SendObjectstomoveWithDelay());
}};
private bool randomDelay = false;
public bool RandomDelay { get => randomDelay; set {
SpeedUpdater(value);
randomDelay = value;
}};
For that I would make also that SpeedUpdater() accepts the bool argument:
private void SpeedUpdater(bool randomSpeedArgument)
{
foreach (WaypointsFollower follower in waypointsFollowers)
{
if (randomSpeedArgument)
{
follower.speed = Random.Range(minRandomSpeed, maxRandomSpeed);
}
else
{
follower.speed = speed;
}
}
}
I was not very careful in adjusting to the logic of your question. The sample code is just to show you how you can run logic on value get/set. Other way are events, a bit more complicated option, and not much needed for this case I believe.
You can give a read to the docs

After Game Over how to stop score from increasing

Im making a endless runner game and am using a 'ScoreManager' object with a box collider 2d set to 'is trigger' increasing the score every time a object hits it. But I want it to stop increasing the score if the health reaches 0. This is the ScoreManager code:
public int score;
public Text scoreDisplay;
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Obstacle"))
{
score++;
}
}
private void Update()
{
scoreDisplay.text = score.ToString();
}
I would like to add a:
public int health = 3;
and in the Update function:
if (health <= 0) {
Destroy(gameObject);
}
But that doesn't seem to work.
The health is displayed in a player script.
public class Player : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeight;
public Text healthDisplay;
public GameObject gameOver;
public int health = 3;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
private void Update()
{
healthDisplay.text = health.ToString();
if (health <= 0) {
gameOver.SetActive(true);
Destroy(gameObject);
}
Any thoughts?
Wherever your health is defined, substitute it by a property that launches an event whenever set to a value < 0. Like this:
public class Player : MonoBehavior
{
public delegate void PlayerDiedDelegate();
public static event PlayerDiedDelegate onPlayerDied;
private int _health;
public int health
{
get
{
return _health;
}
set
{
_health = value;
if(_health < 0)
{
onPlayerDied?.Invoke();
}
}
}
}
Now you can attach any controller in your scene to the event:
public class ScoreController : MonoBehavior
{
private void Awake()
{
Player.onPlayerDied += OnPlayerDied;
}
private void OnPlayerDied()
{
// Put your logic here: stop collecting score etc.
}
}

Damage over time unity

I want to make spell damage over time. So here is my code:
public class Spell : MonoBehaviour
{
public float damage = 1.0f;
public bool ignoreCaster = true;
public float delayBeforeCasting = 0.4f;
public float applyEveryNSeconds = 1.0f;
public int applyDamageNTimes = 5;
private bool delied = false;
private int appliedTimes = 0;
void OnTriggerStay(Collider other)
{
IDamageable takeDamage = other.gameObject.GetComponent<IDamageable>();
if(takeDamage != null)
{
StartCoroutine(CastDamage(takeDamage));
}
}
IEnumerator CastDamage(IDamageable damageable)
{
if(!delied)
{
yield return new WaitForSeconds(delayBeforeCasting);
delied = true;
}
while(appliedTimes < applyDamageNTimes)
{
damageable.TakeDamage(damage);
yield return new WaitForSeconds(applyEveryNSeconds);
appliedTimes++;
}
}
}
Problem is where while starts. I want to check if appliedTimes < applyDamageNTimes, then if it is true to do damage, wait for delay (applyEveryNSeconds) and then check again but i am not handy with coroutine and for some reason it is not doing that.
Here is working code. Also look for other answers if someone need!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spell : MonoBehaviour
{
public float damage = 1.0f;
public bool ignoreCaster = true;
public float delayBeforeCasting = 0.0f;
public float applyEveryNSeconds = 1.0f;
public int applyDamageNTimes = 1;
private bool delied = false;
private int appliedTimes = 0;
private bool test = false;
void OnTriggerStay(Collider other)
{
IDamageable takeDamage = other.gameObject.GetComponent<IDamageable>();
if(takeDamage != null)
{
StartCoroutine(CastDamage(takeDamage));
}
}
IEnumerator CastDamage(IDamageable damageable)
{
if(!test && appliedTimes <= applyDamageNTimes || !test && applyEveryNSeconds == 0)
{
test = true;
if(!delied)
{
yield return new WaitForSeconds(delayBeforeCasting);
delied = true;
}
else
{
yield return new WaitForSeconds(applyEveryNSeconds);
}
damageable.TakeDamage(damage);
appliedTimes++;
test = false;
}
}
}
OnTriggerStay is called every frame 2 objects are colliding. This means that an asynchronous instance of CastDamage coroutine is called every frame. So what happens is that you're getting a whole lot of damage per seconds, which simulates not any damage per second, hehe.
So change OnTriggerStay to OnTriggerEnter.
Depending on the kind of spell it is, I'd rather create a DPS script and apply that to the game object so...
Spell hits Object
AddComponent< MyDamageSpell>();
And then the MyDpsSpell does damage every N seconds to the Object it's on, and removes itself when it is done:
// Spell.cs
void OnTriggerEnter(Collider col) {
// if you don't want more than 1 dps instance on an object, otherwise remove if
if (col.GetComponent<MyDpsAbility>() == null) {
var dps = col.AddComponent<MyDpsAbility>();
dps.Damage = 10f;
dps.ApplyEveryNSeconds(1);
// and set the rest of the public variables
}
}
// MyDpsAbility.cs
public float Damage { get; set; }
public float Seconds { get; set; }
public float Delay { get; set; }
public float ApplyDamageNTimes { get; set; }
public float ApplyEveryNSeconds { get; set; }
private int appliedTimes = 0;
void Start() {
StartCoroutine(Dps());
}
IEnumerator Dps() {
yield return new WaitForSeconds(Delay);
while(appliedTimes < ApplyDamageNTimes)
{
damageable.TakeDamage(damage);
yield return new WaitForSeconds(ApplyEveryNSeconds);
appliedTimes++;
}
Destroy(this);
}
If it's an aura spell where units take damage every second they're in range you could do something like:
float radius = 10f;
float damage = 10f;
void Start() {
InvokeRepeating("Dps", 1);
}
void Dps() {
// QueryTriggerInteraction.Collide might be needed
Collider[] hitColliders = Physics.OverlapSphere(gameObject.position, radius);
foreach(Collider col in hitColliders) {
col.getComponent<IDamagable>().TakeDamage(10);
}
}
https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
Why not create, instead a system that applies auras to in-game characters? For example, if a spell like this adds a damage over time debuff it could instead add the debuff-aura script to the game object and then remove itself once its conditions are met.

unity turn base script for enemy

I have a list of enemys. so i want each enemy have their turn.
First of all :
Player turn --> enemys turn ("here each enemy move one by one untill the end then player move again"). how do i making some waiting time here and forcus on enemy turn?
Any help would be appreciated.
void Start()
{
// find list enemy
enemy = GameObject.FindGameObjectsWithTag("Enemy");
}
void Update()
{
//enemy turn reference to player. after move all enemy we change it to false to change the player turn.
if(StaticClass.enemyTurn == true )
{
for(int i=0;i<enemy.length;i++)
{
// how do i making some waiting time here and forcus on enemy turn?
EnemyTurn(i);
}
}
}
public void EnemyTurn(int id)
{
ChessMoveMent chessMoveScript = enemy[id].GetComponent<ChessMoveMent>();
chessMoveScript.ProcessMove();
id++;
if(id>=enemy.Length)
{
isMove = false;
}
}
I usually use StartCoroutine in this case.
Please try the below code:
public IEnumerator EnemyTurn(int id)
{
yield return null;
ChessMoveMent chessMoveScript = enemy[id].GetComponent<ChessMoveMent>();
chessMoveScript.ProcessMove();
id++;
if(id>=enemy.Length)
{
isMove = false;
}
}
When you want to use it, please use with "StartCoroutine()"
StartCoroutine(EnemyTurn(i));
More details here
You might have a coordinator, who tells the participants when it's their turn.
public class GameCoordinator : MonoBehaviour
{
public List<Participant> participants;
private int currentParticipantIdx = -1;
private Participant CurrentParticipant
{
get { return participants[currentParticipantIdx]; }
}
private void Start()
{
PlayNextMove();
}
private void PlayNextMove()
{
ProceedToNextParticipant();
CurrentParticipant.OnMoveCompleted += OnMoveCompleted;
CurrentParticipant.BeginMove();
}
private void OnMoveCompleted()
{
CurrentParticipant.OnMoveCompleted -= OnMoveCompleted;
StartCoroutine(PlayNextMoveIn(2.0f));
}
private IEnumerator PlayNextMoveIn(float countdown)
{
yield return new WaitForSeconds(countdown);
PlayNextMove();
}
private void ProceedToNextParticipant()
{
++currentParticipantIdx;
if (currentParticipantIdx == participants.Count)
currentParticipantIdx = 0;
}
}
public class Participant : MonoBehaviour
{
public event Action OnMoveCompleted;
public void BeginMove()
{
//
}
}

Randomly spawn power ups using a timer xna c#

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.

Categories

Resources