I am getting a StackOverflowException in my C# program.
Cmodel.cs
public class CModel
{
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public Vector3 Scale { get; set; }
public Model Model { get; private set; }
public BoundingSphere BoundingSphere
{
get
{
// no need for rotation, as this is a sphere
Matrix worldTransform = Matrix.CreateScale(Scale) *
Matrix.CreateTranslation(Position); // THIS IS WHERE THE EXCEPTION OCCURS
BoundingSphere transformed = BoundingSphere;
transformed = transformed.Transform(worldTransform);
return transformed;
}
}
private Matrix[] modelTransforms;
private GraphicsDevice graphicsDevice;
private BoundingSphere boundingsphere;
public CModel(Model Model, Vector3 Position, Vector3 Rotation,
Vector3 Scale, GraphicsDevice graphicsDevice)
{
this.Model = Model;
modelTransforms = new Matrix[Model.Bones.Count];
Model.CopyAbsoluteBoneTransformsTo(modelTransforms);
buildBoundingSphere();
}
public void Draw(Matrix View, Matrix Projection)
{
// Calculate the base transformation by combining
// translation, rotation, and scaling
Matrix baseWorld = Matrix.CreateScale(Scale)
* Matrix.CreateFromYawPitchRoll(
Rotation.Y, Rotation.X, Rotation.Z)
* Matrix.CreateTranslation(Position);
foreach (ModelMesh mesh in Model.Meshes)
{
Matrix localWorld = modelTransforms[mesh.ParentBone.Index]
* baseWorld;
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
BasicEffect effect = (BasicEffect)meshPart.Effect;
effect.World = localWorld;
effect.View = View;
effect.Projection = Projection;
effect.EnableDefaultLighting();
}
mesh.Draw();
}
}
private void buildBoundingSphere()
{
BoundingSphere sphere = new BoundingSphere(Vector3.Zero, 0);
// Merge all the model's built in bounding spheres
foreach (ModelMesh mesh in Model.Meshes)
{
BoundingSphere transformed = mesh.BoundingSphere.Transform(
modelTransforms[mesh.ParentBone.Index]);
sphere = BoundingSphere.CreateMerged(sphere, transformed);
}
this.boundingsphere = sphere;
}
}
}
You have a recursive call in your getter which will call itself and cause the StackOverflowException :
public BoundingSphere BoundingSphere
{
get
{
...
BoundingSphere transformed = BoundingSphere;
...
}
}
It's not completely clear what you meant to write - but if you want to preserve any state you will need a backing field for storing the bounding sphere instead.
Your get method calls itself: BoundingSphere calls BoundingSphere.
private BoundingSphere _boundingsphere = null;
public BoundingSphere BoundingSphere
{
get
{
// no need for rotation, as this is a sphere
**Matrix worldTransform = Matrix.CreateScale(Scale)
* Matrix.CreateTranslation(Position);**
BoundingSphere transformed = _boundingsphere;
transformed = transformed.Transform(worldTransform);
return transformed;
}
set
{
_boundingsphere = value;
}
}
When you use following form:
public BoundingSphere BoundingSphere { get; set }
you don't need to specify variable to store actual value, but when you implement get or set explicitly you should declare additional variable and use it in get, set implementations.
Look at the call stack or the exception's stack trace.
Identify the loop.
Figure out how to break the loop.
I think you're needing to change
get
{
...
BoundingSphere transformed = BoundingSphere; // public property
...
}
to this
get
{
...
BoundingSphere transformed = boundingsphere; // private variable
...
}
Related
I have a bunch of different kind of NPCs in my game and of course they logically similar, they have health, they have vision, they can navigate using agent and stuff.
But each NPC type has it's own custom behavior with states, actions, decisions and hooks. And those scripts require various specific data like coroutines running, target altitude or current leaping direction.
And I have to store it or have it on NPC mono behavior, so it is accessible inside state's scripts (they are scriptable objects called from NPC mono behavior)
Right now what I do is specifying array for each data type and count of it that I assign on NPC prefab. And it feels wrong...
public class Npc : MonoBehaviour
{
public static Dictionary<int, Npc> npcs = new Dictionary<int, Npc>();
public int npcId;
public NpcType type;
public Transform shootOrigin;
public Transform head;
public float maxHealth = 50f;
public float visionRange = 15;
public float visionAngle = 60;
public float headAngle = 120;
public float movementSpeed = 4.5f;
public int indexedActionsCount = 0;
[HideInInspector] public float[] lastActTimeIndexed;
[HideInInspector] public bool[] wasActionCompletedIndexed;
public int indexedVector3DataCount = 0;
[HideInInspector] public Vector3[] vector3DataIndexed;
public int indexedFloatDataCount = 0;
[HideInInspector] public float[] floatDataIndexed;
public int indexedBoolDataCount = 0;
[HideInInspector] public bool[] boolDataIndexed;
public int indexedCoroutineDataCount = 0;
[HideInInspector] public IEnumerator[] coroutineDataIndexed;
public NpcState currentState;
public NpcState remainState;
public float Health { get; private set; }
[HideInInspector] public NavMeshAgent agent;
public static int decisionUpdatesPerSecond = 2; // Check for decisions in 2FPS
public static int actionUpdatesPerSecond = 5; // Act in 5FPS
public static int reportUpdatesPerSecond = 15; // Report in 15FPS
private static int nextNpcId = 10000;
public void Awake()
{
agent = GetComponent<NavMeshAgent>();
}
public void Start()
{
npcId = nextNpcId;
nextNpcId++;
npcs.Add(npcId, this);
Health = maxHealth;
agent.speed = movementSpeed;
lastActTimeIndexed = new float[indexedActionsCount];
wasActionCompletedIndexed = new bool[indexedActionsCount];
floatDataIndexed = new float[indexedFloatDataCount];
boolDataIndexed = new bool[indexedBoolDataCount];
vector3DataIndexed = new Vector3[indexedVector3DataCount];
coroutineDataIndexed = new IEnumerator[indexedCoroutineDataCount];
ServerSend.SpawnNpc(npcId, type, transform.position);
InvokeRepeating("GetTarget", 1.0f, 1.0f);
InvokeRepeating("UpdateDecisions", 0.0f, 1.0f / decisionUpdatesPerSecond);
InvokeRepeating("UpdateActions", 0.0f, 1.0f / actionUpdatesPerSecond);
InvokeRepeating("SendUpdates", 0.0f, 1.0f / reportUpdatesPerSecond);
OnEnterState();
}
public void TakeDamage(float _damage)
{
}
public bool GoTo(Vector3 location)
{
}
public void TransitionToState(NpcState nextState)
{
OnExitState();
currentState = nextState;
OnEnterState();
}
public void StartCoroutineOnNpc(IEnumerator routine)
{
StartCoroutine(routine);
}
public void StopCoroutineOnNpc(IEnumerator routine)
{
StopCoroutine(routine);
}
private void OnEnterState()
{
var hooks = currentState.onEnterHooks;
for (int i = 0; i < hooks.Length; i++)
{
hooks[i].Apply(this);
}
stateTimeOnEnter = Time.time;
wasActionCompleted = false;
}
private void OnExitState()
{
var hooks = currentState.onExitHooks;
for (int i = 0; i < hooks.Length; i++)
{
hooks[i].Apply(this);
}
}
private void UpdateDecisions()
{
currentState.UpdateDecisions(this);
}
private void UpdateActions()
{
currentState.UpdateState(this);
}
private void SendUpdates()
{
ServerSend.NpcState(this);
}
}
In JavaScript world I would just have 1 array or object and put any data this particular NPC needs to it. But in C# I need a strongly typed place to put data for each data type my scripts could require.
Example of data usage in script:
I don't think having so many arrays and counters on MonoBehavior is a good idea, especially that there may be a lot of NPCs on scene. Any advice on building better storage while maintaining script flexibility?
Clarification:
All the behavior logic is controlled by flexible ScriptableObject states. The problem is these objects cannot store any runtime data, but they have access to my Npc MonoBehavior (component) instance.
Initial code for this approach came from Unity tutorial
Let me explain the structure I ended up using for the case I described:
If particular NPC requires some specific data for its behavior I will add another component (in this example leaper NPC needs to store data for leaping behavior)
This data is defined in interface (it's important, because 1 NPC may implement multiple interfaces [several reused behaviors])
public interface ILeaperData
{
public Vector3 leapTarget { get; set; }
public Vector3 initialPosition { get; set; }
public bool startedLeap { get; set; }
public float lastLeapTime { get; set; }
}
And then this NPC type will have component that implements this interface (and 1 more in this example)
public class LeaperData : NpcData, ILeaperData, ICompletedActionData
{
public Vector3 leapTarget { get; set; }
public Vector3 initialPosition { get; set; }
public bool startedLeap { get; set; }
public float lastLeapTime { get; set; }
public bool wasActionCompleted { get; set; }
}
That way I can reuse data interfaces when the same behavior is used on other NPC types.
Example of how it is used in ScriptableObject logic:
[CreateAssetMenu(menuName = "AI/Decisions/CanLeap")]
public class CanLeapDecision : NpcDecision
{
public int nextAngle = 45;
public float radius = 4;
public override bool Decide(Npc npc)
{
if (npc.target)
{
var dir = (npc.transform.position - npc.target.position).normalized;
var dir2 = new Vector2(dir.x, dir.z).normalized * radius;
var dir3 = new Vector3(dir2.x, dir.y, dir2.y);
if (NavMesh.SamplePosition(RotateAroundPoint(npc.target.position + dir3, npc.target.position, Quaternion.Euler(0, nextAngle * ((Random.value > 0.5f) ? 1 : -1), 0)), out var hit, 3.5f, 1))
{
var path = new NavMeshPath();
npc.agent.CalculatePath(hit.position, path);
if (path.corners.Length == 2 && path.status == NavMeshPathStatus.PathComplete)
{
((ILeaperData)npc.npcData).leapTarget = hit.position;
((ILeaperData)npc.npcData).initialPosition = npc.transform.position;
((ILeaperData)npc.npcData).startedLeap = false;
return true;
}
}
}
return false;
}
private Vector3 RotateAroundPoint(Vector3 point, Vector3 pivot, Quaternion angle)
{
var finalPos = point - pivot;
//Center the point around the origin
finalPos = angle * finalPos;
//Rotate the point.
finalPos += pivot;
//Move the point back to its original offset.
return finalPos;
}
}
You can see the cast to (ILeaperData) where I need the data stored on this NPC instance.
I could do with some help. I have a radar which displays objects. I'm trying to introduce a radar scan feature so that when a button is clicked the image on the radar is updated based on the tag of the object. My code has no errors but I can't get it to work and was hoping someone here could spot what the problem is. Thanks!!!!
RadarScan Script
public class RadarScan : MonoBehaviour {
public Image RadarImageToChange;
public void ChangeImage(Image UpdateImage)
{
if(gameObject.tag == "Enemy")
{
UpdateImage = RadarImageToChange;
}
}
Radar Script
public class RadarObject
{
public Image icon { get; set; }
public GameObject owner { get; set; }
}
public class Radar : MonoBehaviour {
public Transform playerPos; //position of player
float mapScale = 0.1f; //scale radar size
public static List<RadarObject> radObjects = new List<RadarObject>();
//Registers Object to the radar
public static void RegisterRadarObject(GameObject o, Image i)
{
Image image = Instantiate(i);
radObjects.Add(new RadarObject() { owner = o, icon = image }); //adds to List
}
//It loops through the list looking for the owner existing in the list, when it finds the owner is detroys the icon
public static void RemoveRadarObject(GameObject o)
{
//New list for destroyed objects
List<RadarObject> newList = new List<RadarObject>();
for (int i = 0; i < radObjects.Count; i++)
{
if (radObjects[i].owner == o)
{
Destroy(radObjects[i].icon);
continue;
}
else
newList.Add(radObjects[i]);
}
radObjects.RemoveRange(0, radObjects.Count);
radObjects.AddRange(newList);
}
void DrawRadarDots()
{
//loops through the list and for each Object it gets the owners transform position and determins the difference between it's
//position and the players position, does calculations on the angle and distance and position on a circle using polar equations.
foreach (RadarObject ro in radObjects)
{
Vector3 radarPos = (ro.owner.transform.position - playerPos.position);
float distToObject = Vector3.Distance(playerPos.position, ro.owner.transform.position) * mapScale;
float deltay = Mathf.Atan2(radarPos.x, radarPos.z) * Mathf.Rad2Deg - 270 - playerPos.eulerAngles.y;
radarPos.x = distToObject * Mathf.Cos(deltay * Mathf.Deg2Rad) * -1;
radarPos.z = distToObject * Mathf.Sin(deltay * Mathf.Deg2Rad);
//grabs icon of players objects and make it a child of panel and set it's postion based on radarPos.x and radarPos.z
ro.icon.transform.SetParent(this.transform);
ro.icon.transform.position = new Vector3(radarPos.x, radarPos.z, 0) + this.transform.position;
}
}
//Update is called once per frame
void Update ()
{
DrawRadarDots();
}
}
MakeRadarObject Script
public class MakeRadarObject : MonoBehaviour {
public Image image;
// Use this for initialization
void Start () {
Radar.RegisterRadarObject(this.gameObject, image);
}
void OnDestroy()
{
Radar.RemoveRadarObject(this.gameObject);
}
}
You aren't applying the Image to your gameobject, only to a variable named UpdateImage. You need to get the image component of your gameobject and then assign the new image to it. You will also need to change the Image to the form of a Sprite for this to work.
public Sprite RadarImageToChange;
public void ChangeImage(Sprite UpdateImage)
{
if(gameObject.tag == "Enemy")
{
gameObject.GetComponent<Image>().sprite = RadarImageToChange;
}
}
I know that this is a pretty obvious question but from what I've seen I can't yet get it.
I don't exactly understand how Getters and Setters work in C#, for instance, I have this code for a character in a game I'm supposed to make for college:
namespace Clase_25_3_2014
{
class Brick
{
private int speed { get; set; }
private Vector2 pos { get; set; }
private Texture2D skin { get; set; }
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed(speed);
this.Pos(position);
this.Skin(skin);
}
}
}
Now, as it stands, where I use the class I try to call it like this:
Brick brick = new Brick(1, Vector2.Zero, mySkin);
brick.GetPos();
Now, obviously that looks weird for you guys, and that's because I haven't yet found out how am I supposed to use it correctly so that it works like a brick.getPos(); from java.
Sorry for the really obvious question, but I can't seem to find an answer for me.
You can't do GetPos because pos is private and you don't have a method called "GetPos". If you make pos public, you can just use Brick.pos to get and Brick.pos(position) to set.
Here's how you could write this class:
namespace Clase_25_3_2014
{
class Brick
{
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed = speed;
this.Pos = position;
this.Skin = skin;
}
public int Speed { get; set; }
public Vector2 Pos { get; set; }
public Texture2D Skin { get; set; }
}
}
Types of Class access:
// lots of explicity (is that a word? :)
public MyClass
{
// Field
// It is recommended to never expose these as public
private int _myField;
// Property (old school, non-auto private field)
public int MyProperty
{
public get
{
return this._myField;
}
public set
{
this._myField = value;
}
}
// Property (new school, auto private field)
// (auto field cannot be accessed in any way)
public int MyProperty2 { public get; private set; }
// Method (these are not needed to get/set values for fields/properties.
public int GetMyMethod()
{
return this._myField;
}
}
var myClass = new MyClass;
// this will not compile,
// access modifier says private
// Set Field value
myClass._myField = 1;
// Get Property Value
var a = myClass.MyProperty;
// Set Property Value
myClass.MyProperty = 2;
// Get Property Value
var b = myClass.MyProperty2;
// this will not compile
// access modifier says private
// Set Property Value
myClass.MyProperty2 = 3;
// Call method that returns value
var c = myClass.GetMyMethod();
When you declare an auto-property in C#, it will get compiled behind the scenes into get and set methods, but you do not need to even think about those. You can access the property as if it were a field.
The benefit of having the property is that you can easily swap it out for a more conventional property with a backing field so that you can provide custom logic in the getter and/or setter. Until you need that, however, it is just extra noise. The auto-property provides the syntactic sugar to avoid that noise.
Brick brick = new Brick(1, Vector2.Zero, mySkin);
Vector2 oldPos = brick.pos;
brick.pos = new Vector2.One;
Try changing to this:
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed = speed;
this.Pos = position;
this.Skin = skin;
}
And with C# you don't need this type of constructors. You can declare an object of this class without constructor with the following way:
public Brick brickTest(){
Speed = 10,
Position = new Vector2(),
Skin = new Texture2D()
};
namespace Clase_25_3_2014
{
class Brick
{
public int Speed { get; set; }
public Vector2 Pos { get; set; }
public Texture2D Skin { get; set; }
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed = speed;
this.Pos = position;
this.Skin = skin;
}
}
}
Using outside:
Brick brick = new Brick(1, Vector2.Zero, mySkin);
Console.WriteLine(Brick.Pos);
however, Behind the scenes the compiler create for each Property:
Private variable storage of the Property-type.
Two function (Get\Set).
Translate the property used to their Functions.
You should just do:
Brick brick = new Brick(1, Vector2.Zero, mySkin);
Vector2 vec = brick.pos;
First thing to get out of the way, sorry if I'm missing something (very) obvious, I'm still kinda new at this.
Anyway, I've been working on an asteroids clone in XNA, and for some reason it would occasionally not start if I hit the Start Debugging button. I tracked the problem to my AsteroidsManager class, which takes an int of initial asteroids to generate, minimum and maximum velocities and rotational velocites, and two texture lists for asteroids and particles. Now the weirdness:
temp = new AsteroidManager(1, 20, 50, 1, 2, asteroids, particles, true); //With this constructor, the game always starts fine...
But if I crank up the number of initial asteroids:
temp = new AsteroidManager(10, 20, 50, 1, 2, asteroids, particles, true); //This seems to start about 1/3 times in the Visual Studio debugger, but if I launch it without debugging or from the bin folder, it works fine.
And lastly, if I set the asteroids to more than ~20, it never starts in the debugger, and if I try to start it from the folder, the process appears in the task manager, but nothing ever happens. Or it just crashes on launch. I honestly have no idea what's causing this, and will gladly provide any code if needed, but here's what I think is relevant:
Full AsteroidManager:
public class AsteroidManager
{
#region Declarations
public List<GameObject> Asteroids { get; set; }
public bool RegenerateAsteroids { get; set; }
public readonly int InitialAsteroids;
public readonly float MinVelocity;
public readonly float MaxVelocity;
public readonly float MinRotationalVelocity; //in degrees
public readonly float MaxRotationalVelocity; //in degrees
public readonly List<Texture2D> Textures;
public readonly List<Texture2D> ExplosionParticleTextures;
List<ParticleEmitter> emitters;
Random rnd;
const int MINPARTICLES = 50;
const int MAXPARTICLES = 200;
const int PARTICLEFTL = 40;
#endregion
public AsteroidManager(
int initialAsteroids,
float minVel,
float maxVel,
float minRotVel,
float maxRotVel,
List<Texture2D> textures,
List<Texture2D> explosionParticleTextures,
bool regenAsteroids)
{
rnd = new Random();
InitialAsteroids = initialAsteroids;
MinVelocity = minVel;
MaxVelocity = maxVel;
MinRotationalVelocity = minRotVel;
MaxRotationalVelocity = maxRotVel;
Textures = textures;
ExplosionParticleTextures = explosionParticleTextures;
RegenerateAsteroids = regenAsteroids;
Asteroids = new List<GameObject>();
emitters = new List<ParticleEmitter>();
for (int i = 0; i < InitialAsteroids; i++)
addAsteroid();
}
public void Update(GameTime gameTime)
{
for (int i = 0; i < Asteroids.Count; i++)
Asteroids[i].Update(gameTime);
for (int i = 0; i < emitters.Count; i++)
emitters[i].Update(gameTime);
if (Asteroids.Count < InitialAsteroids && RegenerateAsteroids)
addAsteroid();
}
public void Draw(SpriteBatch spriteBatch)
{
for (int i = 0; i < Asteroids.Count; i++)
Asteroids[i].Draw(spriteBatch);
for (int i = 0; i < emitters.Count; i++)
emitters[i].Draw(spriteBatch);
}
public void DestroyAsteroid(GameObject asteroid)
{
int x = rnd.Next(MINPARTICLES, MAXPARTICLES);
List<Color> colors = new List<Color>();
colors.Add(Color.White);
emitters.Add(new ParticleEmitter( //TODO: Test
x,
asteroid.WorldCenter,
ExplosionParticleTextures,
colors,
PARTICLEFTL,
true,
1,
x,
1f,
0.3f,
0f,
180f));
Asteroids.Remove(asteroid);
}
protected void addAsteroid()
{
GameObject tempAsteroid;
bool isOverlap = false;
do //Do-While to ensure that the asteroid gets generated at least once
{
Texture2D text = Textures.PickRandom<Texture2D>();
float rot = MathHelper.ToRadians((float)rnd.NextDouble(0f, 359f));
float rotVel = MathHelper.ToRadians((float)rnd.NextDouble(MinRotationalVelocity, MaxRotationalVelocity));
int colRadius = (((text.Width / 2) + (text.Height / 2)) / 2); //Get the mean of text's height & width
Vector2 vel = Vector2.Multiply( //calculate a random velocity
rot.RotationToVectorFloat(),
(float)rnd.NextDouble(MinVelocity, MaxVelocity));
Vector2 worldPos = new Vector2(
rnd.Next(Camera.WorldRectangle.X, Camera.WorldRectangle.Width),
rnd.Next(Camera.WorldRectangle.Y, Camera.WorldRectangle.Height));
tempAsteroid = new GameObject( //init a temporary asteroid to check for overlaps
text, worldPos, vel, Color.White, false, rot, rotVel, 1f, 0f, colRadius);
foreach (GameObject asteroid in Asteroids)
{
if (tempAsteroid.BoundingBox.Intersects(asteroid.BoundingBox))
{
isOverlap = true;
break;
}
}
} while (isOverlap); //if overlapping, loop
Asteroids.Add(tempAsteroid); //add the temp asteroid
}
}
Full GameObject:
public class GameObject
{
#region Declarations
public Texture2D Texture { get; set; }
public Vector2 Origin { get; set; }
public Color TintColor { get; set; }
public float Rotation { get; set; } //radians
public float RotationalVelocity { get; set; }
public float Scale { get; set; }
public float Depth { get; set; }
public bool Active { get; set; }
public SpriteEffects Effects { get; set; }
public Vector2 WorldLocation { get; set; }
public Vector2 Velocity { get; set; }
public int CollisionRadius { get; set; } //Radius for bounding circle collision
public int BoundingXPadding { get; set; }
public int BoundingYPadding { get; set; } //Padding for bounding box collision
public int TotalFrames
{
get //simple get
{ return totalFrames; }
set //check if given totalFrames is in possible range
{
if (value <= (Rows * Columns))
totalFrames = value;
else
throw new ArgumentOutOfRangeException();
}
} //Used in spritesheet animation
private int totalFrames;
public int CurrentFrame
{
get { return currentFrame; }
set
{
currentFrame = (int)MathHelper.Clamp(value, 0, totalFrames);
}
}
private int currentFrame;
public int Rows { get; set; }
public int Columns { get; set; }
public bool Animating { get; set; }
public float RotationDegrees
{
get { return MathHelper.ToDegrees(Rotation); }
set { Rotation = MathHelper.ToRadians(value); }
}
public float RotationVelocityDegrees
{
get { return MathHelper.ToDegrees(RotationalVelocity); }
set { RotationalVelocity = MathHelper.ToRadians(value); }
}
public const float VELOCITYSCALAR = 1.0f / 60.0f; //Default to 60fps standard movement
#endregion
#region Properties
public int GetWidth { get { return Texture.Width / Columns; } } //Width of a frame
public int GetHeight { get { return Texture.Height / Rows; } } //Height of a frame
public int GetRow { get { return (int)((float)CurrentFrame / (float)Columns); } } //Current row
public int GetColumn { get { return CurrentFrame % Columns; } } //Current column
public Vector2 SpriteCenter
{ get { return new Vector2(GetWidth / 2, GetHeight / 2); } } //Get this Sprite's center
public Rectangle WorldRectangle //get rectangle in world coords with width of sprite
{
get
{
return new Rectangle(
(int)WorldLocation.X,
(int)WorldLocation.Y,
GetWidth,
GetHeight);
}
}
public Rectangle BoundingBox //get bounding box for use in collision detection
{
get
{
return new Rectangle( //Get bounding box with respect to padding values
(int)WorldLocation.X + BoundingXPadding,
(int)WorldLocation.Y + BoundingYPadding,
GetWidth - (BoundingXPadding * 2),
GetHeight - (BoundingYPadding * 2));
}
}
public Vector2 ScreenLocation
{ get { return Camera.GetLocalCoords(WorldLocation); } } //get screen coordinates
public Rectangle ScreenRectangle
{ get { return Camera.GetLocalCoords(WorldRectangle); } } //get screen rectangle
public Vector2 WorldCenter
{
get { return WorldLocation + SpriteCenter; }
set { WorldLocation = value - SpriteCenter; }
} //gets/sets the center of the sprite in world coords
public Vector2 ScreenCenter
{ get { return Camera.GetLocalCoords(WorldLocation + SpriteCenter); } } //returns the center in screen coords
#endregion
public GameObject( //main constructor, /w added optional parameters and call to SpriteBase init
Texture2D texture,
Vector2 worldLocation,
Vector2 velocity,
Color tintColor,
bool animating = false,
float rotation = 0f, //default to no rotation
float rotationalVelocity = 0f,
float scale = 1f, //default to 1:1 scale
float depth = 0f, //default to 0 layerDepth
int collisionRadius = 0, //collision radius used in bounding circle collision, default to 0 or no bounding circle
int xPadding = 0, //amount of x padding, used in bounding box collision, default to 0, or no bounding box
int yPadding = 0, //amount of y padding, used in bounding box collision, default to 0, or no bounding box
SpriteEffects effects = SpriteEffects.None,
int totalFrames = 0,
int rows = 1,
int columns = 1)
{
if (texture == null) { throw new NullReferenceException("Null texture reference."); }
Texture = texture; //assign parameters
WorldLocation = worldLocation;
TintColor = tintColor;
Rotation = rotation;
RotationalVelocity = rotationalVelocity;
Scale = scale;
Depth = depth;
Effects = effects;
Velocity = velocity;
Animating = animating;
Active = true;
BoundingXPadding = xPadding; BoundingYPadding = yPadding; CollisionRadius = collisionRadius; //assign collision data
Rows = rows; Columns = columns; this.TotalFrames = totalFrames; //assign animation data
Origin = SpriteCenter; //assign origin to the center of a frame
}
#region Methods
public virtual void Update(GameTime gameTime)
{
if (Active) //if object is active
{
WorldLocation += Velocity * (1f / 60f);
Rotation += RotationalVelocity; //Rotate according to the velocity
//Move by Velocity times a roughly 60FPS scalar
if (TotalFrames > 1 && Animating)
{
CurrentFrame++;
if (CurrentFrame >= TotalFrames)
CurrentFrame = 0; //Loop animation
}
if (Camera.IsObjectInWorld(this.WorldRectangle) == false)
{
if (Camera.LOOPWORLD) //if world is looping and the object is out of bounds
{
Vector2 temp = WorldCenter; //temporary Vector2 used for updated position
//X-Axis Component
if (WorldCenter.X > Camera.WorldRectangle.Width)
temp.X = Camera.WorldRectangle.X - (GetWidth / 2); //If X is out of bounds to the right, move X to the left side
if (WorldCenter.X < Camera.WorldRectangle.X)
temp.X = Camera.WorldRectangle.Width + (GetWidth / 2); //If X is out of bound to the left, move X to the right side
//Y-Axis Component
if (WorldCenter.Y > Camera.WorldRectangle.Height)
temp.Y = Camera.WorldRectangle.Y - (GetHeight / 2); //If Y is out of bounds to the bottom, move Y to the top
if (WorldCenter.Y < Camera.WorldRectangle.Y)
temp.Y = Camera.WorldRectangle.Height + (GetHeight / 2); //If Y is out of bounds to the top, move Y to the bottom
WorldCenter = temp; //Assign updated position
}
if (Camera.LOOPWORLD == false)
{
Active = false; //if the object is outside the world but the LOOPWORLD constant is false, set inactive
}
}
}
}
public virtual void Draw(SpriteBatch spriteBatch)
{
if (Active)
{
if (TotalFrames > 1 && Camera.IsObjectVisible(WorldRectangle)) //if multi-frame animation & object is visible
{
Rectangle sourceRectangle = new Rectangle(GetWidth * GetColumn,
GetHeight * GetRow, GetWidth, GetHeight); //get source rectangle to use
spriteBatch.Draw(
Texture,
ScreenCenter,
sourceRectangle, //use generated source rectangle
TintColor,
Rotation,
Origin,
Scale,
Effects,
Depth);
}
else //if single frame sprite
{
if (Camera.IsObjectVisible(WorldRectangle)) //check if sprite is visible to camera
{
spriteBatch.Draw(
Texture,
ScreenCenter, //center of the sprite in local coords
null, //full sprite
TintColor,
Rotation,
Origin,
Scale,
Effects, //spriteeffects
Depth); //layerdepth
}
}
}
}
public bool IsBoxColliding(Rectangle obj) //bounding box collision test
{
return BoundingBox.Intersects(obj);
}
public bool IsBoxColliding(GameObject obj) //overload of previous which takes a GameObject instead of a rectangle
{
if (BoundingBox.Intersects(obj.BoundingBox))
return true;
else
return false;
}
public bool IsCircleColliding(Vector2 objCenter, float objRadius)
{
if (Vector2.Distance(WorldCenter, objCenter) <
(CollisionRadius + objRadius)) //if the distance between centers is greater than the sum
return true; //of the radii, collision has occurred
else
return false; //if not, return false
}
public bool IsCircleColliding(GameObject obj) //overload of previous which takes a GameObject instead of loose values
{
if (Vector2.Distance(this.WorldCenter, obj.WorldCenter) <
(CollisionRadius + obj.CollisionRadius))
return true;
else
return false;
}
public void RotateTo(Vector2 point) //rotates the GameObject to a point
{
Rotation = (float)Math.Atan2(point.Y, point.X);
}
protected Vector2 rotationToVector()
{
return Rotation.RotationToVectorFloat();
} //local version of extension method
#endregion
}
Game1 Draw:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(); //BEGIN SPRITE DRAW
fpsDisplay.Draw(spriteBatch);
temp.Draw(spriteBatch);
spriteBatch.End(); //END SPRITE DRAW
base.Draw(gameTime);
}
Game1 Update:
protected override void Update(GameTime gameTime)
{
InputHandler.Update(); //update InputHandler
if (InputHandler.IsKeyDown(Keys.Left))
Camera.Position += new Vector2(-3f, 0f);
if (InputHandler.IsKeyDown(Keys.Right))
Camera.Position += new Vector2(3f, 0f);
if (InputHandler.IsKeyDown(Keys.Up))
Camera.Position += new Vector2(0f, -3f);
if (InputHandler.IsKeyDown(Keys.Down))
Camera.Position += new Vector2(0f, 3f);
fpsDisplay.Value = (int)Math.Round(1 / gameTime.ElapsedGameTime.TotalSeconds, 0);
//calculate framerate to the nearest int
temp.Update(gameTime);
base.Update(gameTime);
}
I would guess that your overlapping code is never finding a spot to place the asteroid. It enters a near-infinite (or possibly infinite if space is covered properly) loop that never exits. You could use a counter that given a number of attempts, it just "gives up". Or you can increase the max size of the playing area they can spawn in, or decrease their size; this would reduce the likelyhood of such an infinite loop from occurring, but not make it impossible given enough asteroids.
int attempts = 0;
do //Do-While to ensure that the asteroid gets generated at least once
{
attempts++;
...
foreach (GameObject asteroid in Asteroids)
{
if (tempAsteroid.BoundingBox.Intersects(asteroid.BoundingBox))
{
isOverlap = true;
break;
}
}
} while (isOverlap && attempts < 20); //if overlapping, loop, give up after 20 tries
if (attempts == 20)
{
//log it! Or fix it, or something!
}
Even if you "fix" this by increasing the game size or reducing the asteroid size, I still suggest you make it run a maximum number of times to avoid infinite loops.
I'm trying to make a simple isometric game engine but have some problems with the camera. When i have it like this i can see my model from the front. But i want to see it from an isometric perspective. I tried using a lot of methods but none seem to work. Perhaps I got stuck in the code itself? Can you guys help me with the code perhaps?
public class Camera : PositionedObject
{
#region Fields
private Matrix cameraRotation;
#endregion
#region Properties
public Matrix View
{
get;
set;
}
public Matrix Projection
{
get;
protected set;
}
public Vector3 Target
{
get;
set;
}
#endregion
#region Constructor
public Camera(Game game, Vector3 position, Vector3 target, Vector3 rotation, bool Orthographic, float near, float far)
: base(game)
{
Position = position;
RotationInRadians = rotation;
Target = target;
if (Orthographic)
{
Projection = Matrix.CreateOrthographic(Game.Window.ClientBounds.Width, Game.Window.ClientBounds.Height,
near, far);
}
else
{
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
(float)Game.Window.ClientBounds.Width / (float)Game.Window.ClientBounds.Height, near, far);
}
}
#endregion
#region Public Methods
public override void Initialize()
{
base.Initialize();
cameraRotation = Matrix.Identity;
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
cameraRotation = Matrix.CreateFromAxisAngle(cameraRotation.Forward, RotationInRadians.Z)
* Matrix.CreateFromAxisAngle(cameraRotation.Right, RotationInRadians.X)
* Matrix.CreateFromAxisAngle(cameraRotation.Up, RotationInRadians.Y);
Target = Position + cameraRotation.Forward;
View = Matrix.CreateLookAt(Position, Target, cameraRotation.Up);
}
public void Draw(BasicEffect effect)
{
effect.View = View;
effect.Projection = Projection;
}
#endregion
}
The easiest way is to calculate the camera position based on the focus point (a point on your ground, or whatever).
//Lets start with looking at origo for now.
Vector3 FocusPoint = Vector3.Zero;
//This tells us where the camera should be, RELATIVE to the point we are watching.
//I set this a little up and a little back
Vector3 CameraOffset = new Vector3(0f, 20f, 20f);
Matrix ViewMatrix
{
get
{
//The Offset is just up and back, we need to rotate it 45*
var rotatedOffset = Vector3.Transform(CameraOffset, Matrix.CreateRotationY(MathHelper.PiOver2 * 0.5f));
//Now we can create out viewmatrix. No need to use a transformed "up" unless it's not going to be upside down or something.
return Matrix.CreateLookAt(rotatedOffset, FocusPoint, Vector3.Up);
}
}