Optmisation, populate data from memory - c#

I'm trying to populate objects using read memory. I'm using structure and it goes pretty fast, at least it seems like it.
float x = 0;
while (true)
{
st.Start();
List<DataChild> DataChildList = new List<DataChild>();
var Data = Algorithm.Data;
Data.ChildData.pData = IntPtr.Add(Data.ChildData.pData, 0x20);
for (int i = 0; i < Data.NumberOfObjects; i++)
{
ChildData childData = Data.ChildDatas.ReadValue(i, true);
if (childData.Render.X != x)
{
Console.WriteLine("Change detected");
x = childData.Render.X;
}
DataChildList.Add(childData)
}
Algorithm.Data.Datas = DataChildList;
st.Stop();
Console.WriteLine($"populated in {st.Elapsed.TotalMilliseconds} ms");
st.Reset();
}
}
There are my structs :
[StructLayout(LayoutKind.Explicit)]
public struct ChildData
{
[FieldOffset(0x0)]
public IntPtr BasePointer;
[FieldOffset(0x400)]
public IntPtr pRender;
public Vector3 Render
{
get
{
return M.Read<Render>(this.pRender);
}
}
}
[StructLayout(LayoutKind.Explicit)]
public struct Render
{
[FieldOffset(0x30)]
public float X;
public float posX
{
get
{
return this.X;
}
}
}
The StopWatch says I populate the data in 0.005 ms.
When I press a key, it change the value of childData.Render.X (the only one in the list for now).
But it displays it in between 0.5 and 1 sec which seems to be very slow compared to speed of the loop.
Have to say that this code is running into a Thread.
Any ideas on why it takes so long? Thanks!

Related

How to increase the number of lives over time?

I have such a script:
public class SystemLives : MonoBehaviour
{
public int lives;
public int maxLives;
public Image[] Live;
public Sprite FullHearts;
public Sprite EmptyHearts;
public void setLives(int time)
{
lives += Mathf.RoundToInt(Time.deltaTime * time);
if (lives > maxLives)
{
lives = maxLives;
}
for (int i = 0; i < Live.Length; i++)
{
if (i < lives)
{
Live[i].sprite = FullHearts;
}
else
{
Live[i].sprite = EmptyHearts;
}
}
}
public void TakeHit(int damage)
{
lives -= damage;
if (lives <= 0)
{
Debug.Log("Игра окончена");
}
for (int i = 0; i < Live.Length; i++)
{
if (i < Mathf.RoundToInt(lives))
{
Live[i].sprite = FullHearts;
}
else
{
Live[i].sprite = EmptyHearts;
}
}
}
n which there are functions for subtracting and adding life, I hung this script on an empty element, I call it like this:
public GameObject ScriptLives;
private SystemLives systemLives;
public int times=1;
public void Btn()
{
foreach (var s in strings)
{
if (s.Compare() == true)
{
b++;
}
else
{
prob++;
}
}
systemLives = ScriptLives.GetComponent<SystemLives>();
systemLives.setLives(times);
systemLives = ScriptLives.GetComponent<SystemLives>();
systemLives.TakeHit(prob);
Debug.Log($"{b}");
}
I call it like this: It turns out to take lives, but for some reason it is not added. maybe the problem is in the first script? please tell me what the reason may be and how it can be fixed?
You're calling
lives += Mathf.RoundToInt(Time.deltaTime * time);
but Time.deltaTime is the time between frame draws. Even an awful game might still have 10 fps, which means Time.deltaTime is 0.1 seconds. You're passing 1 in as the argument to that function, so (0.1*1) = 0.1.
Then you take 0.1, round it to an integer, so it rounds to 0. Then you increment lives by zero.
I really can't tell what the goal is here with the time dependency on adding lives, so unfortunately I've got no suggestions, but hopefully this helps.
An easy way to ask your question is to use InvokeRepeating, which works as follows:
public void Start()
{
InvokeRepeating(nameof(AddLife), 0f, 2f);
}
public void AddLife() // add 1 life every 2 sec
{
life = Mathf.Min(++life, maxLives);
Debug.Log("Life: "+life);
}
If you want to be time dependent, change the life adjustment algorithm to Time.deltaTime to Time.time.
public void Update()
{
SetLife(3f); // set life every 3 sec for e.g.
}
public void setLives(float repeatTime)
{
life = (int) (Time.time / repeatTime);
}

I am having issues with a wave spawner

I am developing a Tower Defence game and I need a wave spawner. I tried to use Brackeys Wave spawner but it only supports one type of enemy per wave and I tried to make one myself like this:
[System.Serializable]
public class WaveContent
{
public Transform enemy;
public int count;
}
[System.Serializable]
public class Wave
{
public string Name;
public WaveContent[] Enemy;
public float Rate = 5f;
}
instead of this:
[System.Serializable]
public class Wave
{
public string name;
public Transform enemy;
public int count;
public float rate;
}
This is the code from the 40min Brackeys video
using UnityEngine;
using System.Collections;
public class WaveSpawner : MonoBehaviour {
public enum SpawnState { SPAWNING, WAITING, COUNTING };
[System.Serializable]
public class Wave
{
public string name;
public Transform enemy;
public int count;
public float rate;
}
public Wave[] waves;
private int nextWave = 0;
public int NextWave
{
get { return nextWave + 1; }
}
public Transform[] spawnPoints;
public float timeBetweenWaves = 5f;
private float waveCountdown;
public float WaveCountdown
{
get { return waveCountdown; }
}
private float searchCountdown = 1f;
private SpawnState state = SpawnState.COUNTING;
public SpawnState State
{
get { return state; }
}
void Start()
{
if (spawnPoints.Length == 0)
{
Debug.LogError("No spawn points referenced.");
}
waveCountdown = timeBetweenWaves;
}
void Update()
{
if (state == SpawnState.WAITING)
{
state = SpawnState.COUNTING;
/*if (!EnemyIsAlive())
{
WaveCompleted();
}
else
{
return;
}*/
}
if (waveCountdown <= 0)
{
if (state != SpawnState.SPAWNING)
{
StartCoroutine( SpawnWave ( waves[nextWave] ) );
}
}
else
{
waveCountdown -= Time.deltaTime;
}
}
void WaveCompleted()
{
Debug.Log("Wave Completed!");
state = SpawnState.COUNTING;
waveCountdown = timeBetweenWaves;
if (nextWave + 1 > waves.Length - 1)
{
nextWave = 0;
Debug.Log("ALL WAVES COMPLETE! Looping...");
}
else
{
nextWave++;
}
}
bool EnemyIsAlive()
{
searchCountdown -= Time.deltaTime;
if (searchCountdown <= 0f)
{
searchCountdown = 1f;
if (GameObject.FindGameObjectWithTag("Enemy") == null)
{
return false;
}
}
return true;
}
IEnumerator SpawnWave(Wave _wave)
{
Debug.Log("Spawning Wave: " + _wave.name);
state = SpawnState.SPAWNING;
for (int i = 0; i < _wave.count; i++)
{
SpawnEnemy(_wave.enemy);
yield return new WaitForSeconds( 1f/_wave.rate );
}
state = SpawnState.WAITING;
yield break;
}
void SpawnEnemy(Transform _enemy)
{
Debug.Log("Spawning Enemy: " + _enemy.name);
Transform _sp = spawnPoints[ Random.Range (0, spawnPoints.Length) ];
Instantiate(_enemy, _sp.position, _sp.rotation);
}
}
And I need to add this to the code to get what I am expecting
[System.Serializable]
public class WaveContent
{
public Transform enemy;
public int count;
}
[System.Serializable]
public class Wave
{
public string Name;
public WaveContent[] Enemy;
public float Rate = 5f;
}
But I was not able to, can anyone help me?
Thanks in advance,
Dev
You should look at his code, there will be those lines of code :
IEnumerator SpawnWave(Wave _wave)
{
state = SpawnState.SPAWNING;
for (int i = 0; i < wave.count; i++)
{
SpawnEnemy(_wave.enemy);
yield return new WaitForSeconds( 1f/_wave.rate );
}
state = SpawnState.WAITING;
yield break;
}
And also those lines of code :
void SpawnEnemy(Transform _enemy)
{
Debug.Log("Spawning Enemy: " + _enemy.name);
Transform sp = spawnPoints[ Randon.Range(0, spawnPoints.Length) ];
Instantiate(_enemy, _sp.position, _sp.rotation);
}
So what is the problem, the problem is that the SpawnEnemy() method is using only one type of enemies each wave, using _enemy from class Wave which is in [System.Serializable], so my idea is that we will make another class Wave, same same as his code :
[System.Serializable]
public class Wave
{
public string name;
public Transform[] enemies;
public int count;
public float rate;
}
And change a little bit in SpawnEnemy() method, which I will add Random.Rage() to choose a different transform ( or we can say enemy )
void SpawnEnemy(Transform[] _enemies)
{
Debug.Log("Spawning Enemy: " + _enemy.name);
int randomIndex = Random.Range(0, _enemies.Count());
Transform sp = spawnPoints[ Randon.Range(0, spawnPoints.Length) ];
Instantiate(_enemies[randomIndex], _sp.position, _sp.rotation);
}
If having any trouble when doing, just comment below, yeah
After reading through the existing comments, I think you really only need one additional edit within the SpawnWave code to achieve what you are looking for - Add a foreach loop to loop through each Wave Contents object and spawn the appropriate number and type of enemy
Given your updated objects, with one update to a field name for clarity
[System.Serializable]
public class WaveContent
{
public Transform enemy;
public int count;
}
[System.Serializable]
public class Wave
{
public string Name;
public WaveContent[] WaveContents;
public float Rate = 5f;
}
Then you just need to loop through the array of WaveContent and call SpawnEnemy for each one, using the WaveContent.Count for the inner loop.
IEnumerator SpawnWave(Wave _wave)
{
Debug.Log("Spawning Wave: " + _wave.name);
state = SpawnState.SPAWNING;
// Loop through your Wave Contents
foreach (var waveContent in Wave.WaveContents)
{
// Spawn the number and type of enemy for each content object
for (int i = 0; i < waveContent.count; i++)
{
SpawnEnemy(waveContent.enemy);
yield return new WaitForSeconds(1f / _wave.rate);
}
}
state = SpawnState.WAITING;
yield break;
}

How to rotate a game object through an array of Quaternions?

Im trying to rotate a gameobject through an array of Quaternions that I've read in via a CSV file. Its currently not rotating the objectas i believe i'm not updating the transform.rotation correctly. Any help resolving this would be greatly appreciated, let me know if you need anymore information.
RotationAnimator.cs The Rotator Script:
public class RotationAnimator : MonoBehaviour
{
public Transform playbackTarget;
[HideInInspector]
public bool playingBack = false;
public CSVReader csvReader;
public void PlayRecording()
{
// -1 as it reads the last blank line of the CSV for reasons
for (int row = 0; row < csvReader.quaternionRecords.Length-1; row++)
{
playbackTarget.rotation = new Quaternion(csvReader.quaternionRecords[row].x, csvReader.quaternionRecords[row].y, csvReader.quaternionRecords[row].z, csvReader.quaternionRecords[row].w);
}
}
}
CSVReader.cs The script reading the csv file
public class CSVReader : MonoBehaviour
{
public QuaternionRecord[] quaternionRecords;
public List<List<String>> fileData;
ILogging logger;
string path = "Assets\\Logs\\RotationList.csv";
private void OnEnable()
{
logger = Logging.GetCurrentClassLogger();
}
public void ReadFile()
{
var r = File.OpenText(path);
fileData = r.ReadToEnd().Split('\n').Select(s => s.Split(',').ToList()).ToList();
quaternionRecords = new QuaternionRecord[fileData.Count];
// skip last empty line at "file data.count -1"
for(int row = 0; row < fileData.Count-1; row++)
{
try
{
quaternionRecords[row] = new QuaternionRecord();
quaternionRecords[row].time = float.Parse(fileData[row][0]);
quaternionRecords[row].x = float.Parse(fileData[row][1]);
quaternionRecords[row].y = float.Parse(fileData[row][2]);
quaternionRecords[row].z = float.Parse(fileData[row][3]);
quaternionRecords[row].w = float.Parse(fileData[row][4]);
}
catch(Exception e)
{
Debug.Log("Ouch!");
}
}
r.Close();
}
public struct QuaternionRecord
{
public float time;
public float x;
public float y;
public float z;
public float w;
}
}
Where PlayRecording is called:
public void Playback()
{
Debug.Log("Beginning Playback...");
csvReader.ReadFile();
rotationAnimator.PlayRecording();
}
Here is a way to itterate the quaternionRecords using Coroutines by modifying your existing code.
//Change void to IEnumerator
public IEnumerator PlayRecording()
{
for (int row = 0; row < csvReader.quaternionRecords.Length; row++)
{
playbackTarget.rotation = new Quaternion(csvReader.quaternionRecords[row].x, csvReader.quaternionRecords[row].y, csvReader.quaternionRecords[row].z, csvReader.quaternionRecords[row].w);
//Add this line to wait 1 second until next itteration
yield return new WaitForSeconds(1f);
}
}
public void Playback()
{
Debug.Log("Beginning Playback...");
csvReader.ReadFile();
//Change to StartCourotine
StartCoroutine(rotationAnimator.PlayRecording());
}
I haven't tested the code so it might not work and it might not be the best solution but it is a a way to do it. Other ways is using Update with Time.deltaTime

Weird Behaviour when initiate a class

I'm working on a game, and I made all the building blocks. now I'm working on the game logic and rendering.
I have abstract Monster class and a class call GreenMonster that inherits from it.
Now the weird thing is, when I try to init a GreenMonster object.
when I do this:
private void initGreenMonsters()
{
for (int i = 0; i < greenMonsters.Length; i++)
{
greenMonsters[i] = new GreenMonster(new Point(0,40),new Size(40, 40));
}
}
every thing works like I planned and I can render the images on the form.
but when I try to init like that:
private void initGreenMonsters()
{
for (int i = 0; i < greenMonsters.Length; i++)
{
greenMonsters[i] = new GreenMonster();
greenMonsters[i].Position = new Point(0, 40);
greenMonsters[i].Size = new Size(40, 40);
}
}
I don't get any errors, and the app runs, but I can render the monsters.
This is the Monster class constructor and the Draw Method I use to draw a Monster:
public Monster(Point _startPosition,Size _size)
{
this.size = _size;
this.position = _startPosition;
}
public virtual void Draw(Graphics g)
{
Rectangle monsterRec = new Rectangle(position, size);
g.DrawImage(img, monsterRec);
}
and this is the GreenMonster class constructor:
public GreenMonster(Point _startPosition, Size _size)
: base(_startPosition, _size)
{
this.img = new Bitmap(SpaceInvadersGame.Properties.Resources.NormalMonster);
this.hp = 1;
this.speed = 1;
}
public GreenMonster()
{
this.img = new Bitmap(SpaceInvadersGame.Properties.Resources.NormalMonster);
this.hp = 1;
this.speed = 1;
}
the only thing that bothers me is, that when I'm looking at both ways I init the objects, it just looks the same..
I just can't find any different between in both of the ways.
someone have any idea how its different?
If you need more code so the question is more clear, I would be happy to add!
this is the Monster class and its properties
public abstract class Monster
{
protected Point position;
public Point Position { get { return position; } set { position = value; } }
protected Size size;
public Size Size { get { return size; } set { value = size; } }
public int speed;
protected Bitmap img;
protected int hp;
public int HP { get { return hp; } }
public void SetStartingPosition(int x, int y)
{
this.position = new Point(x, y);
}
public virtual void Draw(Graphics g)
{
Rectangle monsterRec = new Rectangle(position, size);
g.DrawImage(img, monsterRec);
}
}
You are setting your incoming value to the current size, rather than setting the current size to the incoming value, in the method below:
public Size Size { get { return size; } set { value = size; } }
should be
public Size Size { get { return size; } set { size = value; } }
Your code for Position looks OK though:
public Point Position { get { return position; } set { position = value; } }

overloading the indexer causes : "Minesweeper.Tile is a type but is used like a variable" error

I am making a minesweeper project in c# for fun and I wanted to store the new Tiles in a dictionary inside the Tile class so that when a Tile is initiated it is stored and can be accessed through Tile[coords] however I keep getting the above error. This is the code I am using for the Tile class (please don't comment on my conventions, I'm new to C# I'm a Java/Python programmer :p)
class Tile
{
private static Dictionary <Coords, Tile> tiles = new Dictionary <Coords, Tile> ();
public int numMinesAdjacents { get; set; }
public readonly bool isMine;
public readonly Coords position;
private Tile [] aAdjacents = new Tile [8];
public Tile(int x, int y, bool isMine = false)
{
this.isMine = isMine;
position = new Coords(x, y);
Tile[position] = this;
}
public void init()
{
calculateAdjacents();
calculateNumMinesAdjacent();
}
private void calculateAdjacents()
{
int i = 0;
for (int y = -1; y < 1; y++)
{
if ((position.y - y) < 0 || (position.y + y) > Math.Sqrt(Program.MAX_TILES)) continue;
for (int x = -1; x < 1; x++)
{
if ((position.x - x) < 0 || (position.x + x) > Math.Sqrt(Program.MAX_TILES)) continue;
aAdjacents [i] = Tile[position + new Coords(x, y)];
i++;
}
}
}
private void calculateNumMinesAdjacent()
{
int n = 0;
foreach (Tile pTile in aAdjacents)
{
if (pTile.isMine) n++;
}
numMinesAdjacents = n;
}
/*private static void add(Tile pTile)
{
tiles.Add(pTile.position, pTile);
}*/
public /*static - if I use static nothing is different*/ Tile this [Coords coords]
{
get { return tiles [coords]; }
}
}
if I call
Tile(0, 0);
Tile(0, 1);
and then
Tile[new Coords(0, 0)]
I get an error, I also get an error in the places in the class where Tile[] is used (constructor and calculateAdjacents) what is going wrong here?
Thanks,
Jamie
EDIT: Sorry I meant Tile[position] I was changing it back and mistyped. The problem is I overloaded this which should mean that Tile[coords] is legal even when called from another class
It's not clear what you expect this to mean:
Tile[this];
It's not a valid expression at the moment though.
C# doesn't support static indexers. For an instance indexer, you could use:
Tile tile = this[someCoordinate];
... although it's odd for an instance indexer to use a static member like this. It would be cleaner just to have a method:
public static Tile GetTile(Coords coords)
{
return tiles[coords];
}
Then you'd just call Tile.GetTile(...) elsewhere.
As a side-note, you should start following .NET naming conventions to make your code easier to understand. Also, I'd strongly recommend that you avoid public fields, even if they're read-only.

Categories