I'm making a game using XNA 4.0 and c#.
I just made my camera class and it's almost working as expected, except for one thing.
I can move with my mouse to rotate the camera but the problem is that when I get to the border of the screen, the mouse stops and so does the camera movement.
Is there any way to allow the mouse pointer to be outside of the screens boundaries to be able to keep tracking the pointer? Or is there any simpler way around?
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
public class ArcBallCamera
{
private float speeder = 100;
public ArcBallCamera(float aspectRation, Vector3 lookAt)
: this(aspectRation, MathHelper.PiOver4, lookAt, Vector3.Up, 0.1f, float.MaxValue) { }
public ArcBallCamera(float aspectRatio, float fieldOfView, Vector3 lookAt, Vector3 up, float nearPlane, float farPlane)
{
this.aspectRatio = aspectRatio;
this.fieldOfView = fieldOfView;
this.lookAt = lookAt;
this.nearPlane = nearPlane;
this.farPlane = farPlane;
}
/// <summary>
/// Recreates our view matrix, then signals that the view matrix
/// is clean.
/// </summary>
public void ReCreateViewMatrix()
{
//Calculate the relative position of the camera
position = Vector3.Transform(Vector3.Backward, Matrix.CreateFromYawPitchRoll(yaw, pitch, 0));
//Convert the relative position to the absolute position
position *= zoom;
position += lookAt;
//Calculate a new viewmatrix
viewMatrix = Matrix.CreateLookAt(position, lookAt, Vector3.Up);
viewMatrixDirty = false;
}
/// <summary>
/// Recreates our projection matrix, then signals that the projection
/// matrix is clean.
/// </summary>
public void ReCreateProjectionMatrix()
{
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(fieldOfView, AspectRatio, nearPlane, farPlane);
projectionMatrixDirty = false;
}
#region HelperMethods
/// <summary>
/// Moves the camera and lookAt at to the right,
/// as seen from the camera, while keeping the same height
/// </summary>
public void MoveCameraRight(float amount)
{
Vector3 right = Vector3.Normalize(LookAt - Position); //calculate forward
right = Vector3.Cross(right, Vector3.Up); //calculate the real right
//right.Y = 0;
right.Normalize();
LookAt += right * amount;
}
/// <summary>
/// Moves the camera and lookAt forward,
/// as seen from the camera, while keeping the same height
/// </summary>
public void MoveCameraForward(float amount)
{
Vector3 forward = Vector3.Normalize(LookAt - Position);
// forward.Y = 0;
forward.Normalize();
LookAt += forward * amount;
}
/// <summary>
/// Moves the camera and lookAt up or down,
/// LOL
/// </summary>
public void MoveCameraUp(float amount)
{
Vector3 up = Vector3.Normalize(LookAt - position);
up = Vector3.Cross(up, Vector3.Left); //Calculate the REAL FUCKING UP/DOWN
up.X = 0;
up.Z = 0;
up.Normalize();
LookAt += up * amount;
}
#endregion
#region FieldsAndProperties
//We don't need an update method because the camera only needs updating
//when we change one of it's parameters.
//We keep track if one of our matrices is dirty
//and reacalculate that matrix when it is accesed.
private bool viewMatrixDirty = true;
private bool projectionMatrixDirty = true;
public float MinPitch = -MathHelper.PiOver2 + 0.3f;
public float MaxPitch = MathHelper.PiOver2 - 0.3f;
private float pitch;
public float Pitch
{
get { return pitch; }
set
{
viewMatrixDirty = true;
pitch = MathHelper.Clamp(value, MinPitch, MaxPitch);
}
}
private float yaw;
public float Yaw
{
get { return yaw; }
set
{
viewMatrixDirty = true;
yaw = value;
}
}
private float fieldOfView;
public float FieldOfView
{
get { return fieldOfView; }
set
{
projectionMatrixDirty = true;
fieldOfView = value;
}
}
private float aspectRatio;
public float AspectRatio
{
get { return aspectRatio; }
set
{
projectionMatrixDirty = true;
aspectRatio = value;
}
}
private float nearPlane;
public float NearPlane
{
get { return nearPlane; }
set
{
projectionMatrixDirty = true;
nearPlane = value;
}
}
private float farPlane;
public float FarPlane
{
get { return farPlane; }
set
{
projectionMatrixDirty = true;
farPlane = value;
}
}
public float MinZoom = 1;
public float MaxZoom = float.MaxValue;
private float zoom = 1;
public float Zoom
{
get { return zoom; }
set
{
viewMatrixDirty = true;
zoom = MathHelper.Clamp(value, MinZoom, MaxZoom);
}
}
private Vector3 position;
public Vector3 Position
{
get
{
if (viewMatrixDirty)
{
ReCreateViewMatrix();
}
return position;
}
}
private Vector3 lookAt;
public Vector3 LookAt
{
get { return lookAt; }
set
{
viewMatrixDirty = true;
lookAt = value;
}
}
#endregion
#region ICamera Members
public Matrix ViewProjectionMatrix
{
get { return ViewMatrix * ProjectionMatrix; }
}
private Matrix viewMatrix;
public Matrix ViewMatrix
{
get
{
if (viewMatrixDirty)
{
ReCreateViewMatrix();
}
return viewMatrix;
}
}
private Matrix projectionMatrix;
public Matrix ProjectionMatrix
{
get
{
if (projectionMatrixDirty)
{
ReCreateProjectionMatrix();
}
return projectionMatrix;
}
}
#endregion
}
You can reset the mouse position for every frame.
Example:
// Center of the window
var resetPosition = new Point(WindowWidth / 2, WindowHeight / 2);
// Get the movement since the last frame
var mouseState = Mouse.GetState();
var mousePosition = new Point(mouseState.X, mouseState.Y);
var delta = mousePosition - resetPosition;
// Reset the mouse position
Mouse.SetPosition(resetPosition.X, resetPosition.Y);
// Use the delta
RotateCamera(delta);
Related
I am trying to create an ellipse in 3D space. Ive used
https://www.youtube.com/watch?v=mQKGRoV_jBc
https://www.youtube.com/watch?v=Or3fA-UjnwU&t=390s
https://www.youtube.com/watch?v=lKfqi52PqHk
to create an ellipse. Now the ellipse is constructed via a xAxis and a yAxis. I need to move the middlepoint of the ellipse a given distance in a certain direction and then tilt the hole plane on which the ellipse is constructed at a certain angle.
Now my idea is to take the transform.position of the constructing empty and offset it in the direction needed and at the distance needed. The idea goes then further by simply rotating the constructing empty the given angle. Unfortunelty I have 0 clue how to. Ive provided you with the Ellipse class creating the Ellipse and the EllipseRenderer-script so you have an idea whats going on.
[System.Serializable]
public class Ellipse
{
float xAxis;
float yAxis;
public Ellipse(float xAxis, float yAxis)
{
this.xAxis = xAxis;
this.yAxis = yAxis;
}
public Vector2 Evaluate(float orbitalProgression)
{
float angle = Mathf.Deg2Rad * 360 * orbitalProgression;
float x = Mathf.Sin(angle) * xAxis;
float y = Mathf.Cos(angle) * yAxis;
return new Vector2(x, y);
}
}
,
public class OrbitMotion : MonoBehaviour
{
public Transform orbitingObject;
public Ellipse orbitPath;
[Range(0f,1f)]
public float orbitProgress = 0f;
public float orbitPeriod = 3f;
public bool orbitActive = false;
void Start()
{
if (orbitingObject == null)
{
orbitActive = false;
return;
}
SetOrbitingObjectPosition();
StartCoroutine(AnimateOrbit());
}
void SetOrbitingObjectPosition()
{
Vector2 orbitPos = orbitPath.Evaluate(orbitProgress);
orbitingObject.position = new Vector3(orbitPos.x, 0, orbitPos.y);
}
IEnumerator AnimateOrbit()
{
if (orbitPeriod < 0.1f)
{
orbitPeriod = 0.1f;
}
float orbitspeed = 1 / orbitPeriod;
while (orbitActive)
{
orbitProgress += Time.deltaTime * orbitspeed;
orbitProgress %= 1f;
SetOrbitingObjectPosition();
yield return null;
}
}
}
and
[RequireComponent(typeof(LineRenderer))]
public class EllipseRenderer : MonoBehaviour
{
[Range(3, 36)]
public int segments = 7;
public LineRenderer lr;
public Ellipse ellipse;
private void Awake()
{
lr = GetComponent<LineRenderer>();
CalculateEllipse();
}
void CalculateEllipse()
{
Vector3[] points = new Vector3[segments + 1];
for (int i = 0; i < segments; i++)
{
Vector2 position2D = ellipse.Evaluate((float)i / (float)segments);
points[i] = new Vector3(position2D.x, position2D.y, 0);
}
points[segments] = points[0];
lr.positionCount = segments + 1;
lr.SetPositions(points);
}
public Vector3 CalculatePosition()
{
}
private void OnValidate()
{
CalculateEllipse();
}
}
if dc is not null i'm using a radius from another script and it's working fine when i'm changing the radius value in the DrawCircle script the transform in RotateAroundTarget is moving smooth to the new radius.
but if dc variable is null i want to use the local radius variable for setting the radius in runtime but when changing the radius the transform is not moving to the new radius.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateAroundTarget : MonoBehaviour
{
public Transform target;
public float rotatingSpeed;
public float movingSpeed;
public Vector3 axis;
public bool randomHeight;
public float setRandomHeight = 1;
public float radius;
public DrawCircle dc;
private float lastRadius;
private bool move = false;
private float t = 0.0f;
public float upperLimit, lowerLimit, delay;
private float prevHeight;
private Vector3 radiusPosition;
private void Start()
{
if (dc != null)
{
lastRadius = dc.xRadius;
}
else
{
lastRadius = radius;
}
move = true;
}
private void Update()
{
if (dc != null)
{
radiusPosition = new Vector3(target.position.x, target.position.y, dc.xRadius);
}
else
{
radiusPosition = new Vector3(target.position.x, target.position.y, radius);
}
if (move == false)
{
transform.RotateAround(target.position, axis, rotatingSpeed * Time.deltaTime);
t += Time.deltaTime;
if (t > delay)
{
prevHeight = setRandomHeight;
setRandomHeight = Random.Range(lowerLimit, upperLimit);
t = 0;
}
var tt = transform.position;
tt.y = Mathf.Lerp(prevHeight, setRandomHeight, t);
transform.position = tt;
}
if (dc != null)
{
if (lastRadius != dc.xRadius)
{
move = true;
lastRadius = dc.xRadius;
}
}
else
{
if (lastRadius != radius)
{
move = true;
lastRadius = radius;
}
}
if (move)
{
if (transform.position != radiusPosition)
{
float step = movingSpeed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position,
radiusPosition, step);
}
else
{
move = false;
}
}
}
}
Im trying to make a game in Monogame and have followed a camera tutorial here https://www.youtube.com/watch?v=c_SPRT7DAeM&t=180s (which works great) The only problem is that when it zooms it zooms to the corner of the player sprite as you can see here:
Here is my code
camera.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace RpgGameCrossPlatform
{
class Camera
{
private Matrix transform;
public Matrix Transform
{
get { return transform; }
}
private Vector2 centre;
private Viewport viewport;
private float zoom = 3;
public float rotation = 0;
public float CentreX
{
get { return centre.X; }
set { centre.X = value; }
}
public float CentreY
{
get { return centre.Y; }
set { centre.Y = value; }
}
public float Zoom
{
get { return zoom; }
set { zoom = value; if (zoom < 0.1f) zoom = 0.1f; }
}
public Camera (Viewport newViewport)
{
viewport = newViewport;
}
public void CameraUpdate(Vector2 position)
{
centre = new Vector2(position.X, position.Y);
transform = Matrix.CreateTranslation(new Vector3(-centre.X, -centre.Y, 0)) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 0)) *
Matrix.CreateTranslation(new Vector3(viewport.Width / 2, viewport.Height / 2, 0));
}
}
}
player.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace RpgGameCrossPlatform
{
class Camera
{
private Matrix transform;
public Matrix Transform
{
get { return transform; }
}
private Vector2 centre;
private Viewport viewport;
private float zoom = 3;
public float rotation = 0;
public float CentreX
{
get { return centre.X; }
set { centre.X = value; }
}
public float CentreY
{
get { return centre.Y; }
set { centre.Y = value; }
}
public float Zoom
{
get { return zoom; }
set { zoom = value; if (zoom < 0.1f) zoom = 0.1f; }
}
public Camera (Viewport newViewport)
{
viewport = newViewport;
}
public void CameraUpdate(Vector2 position)
{
centre = new Vector2(position.X, position.Y);
transform = Matrix.CreateTranslation(new Vector3(-centre.X, -centre.Y, 0)) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 0)) *
Matrix.CreateTranslation(new Vector3(viewport.Width / 2, viewport.Height / 2, 0));
}
}
}
and game.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace RpgGameCrossPlatform
{
class Camera
{
private Matrix transform;
public Matrix Transform
{
get { return transform; }
}
private Vector2 centre;
private Viewport viewport;
private float zoom = 3;
public float rotation = 0;
public float CentreX
{
get { return centre.X; }
set { centre.X = value; }
}
public float CentreY
{
get { return centre.Y; }
set { centre.Y = value; }
}
public float Zoom
{
get { return zoom; }
set { zoom = value; if (zoom < 0.1f) zoom = 0.1f; }
}
public Camera (Viewport newViewport)
{
viewport = newViewport;
}
public void CameraUpdate(Vector2 position)
{
centre = new Vector2(position.X, position.Y);
transform = Matrix.CreateTranslation(new Vector3(-centre.X, -centre.Y, 0)) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 0)) *
Matrix.CreateTranslation(new Vector3(viewport.Width / 2, viewport.Height / 2, 0));
}
}
}
help would be appreciated as well as some general tips as I am new :)
This could mean that the camera is focussing on the player's default position, which is always on top-left.
You should set a center position (called the 'origin') for the player, this could be done by adding half of the player's width (for x) and height (for y) at the current default position it's focussing at.
I am new to coding and Unity
I have the health bar appearing on my screen, but I am not sure how to link the Health script of the health bar to my player script and my player's health script. Simply, I want to make it so when my player gets shot my health bar will lose a heart
my health bar script
using UnityEngine;
using System.Collections;
public class Health : MonoBehaviour {
public int startHealth;
public int healthPerHeart;
private int maxHealth;
private int currentHealth;
public Texture[] heartImages;
public GUITexture heartGUI;
private ArrayList hearts = new ArrayList();
// Spacing:
public float maxHeartsOnRow;
private float spacingX;
private float spacingY;
void Start () {
spacingX = heartGUI.pixelInset.width;
spacingY = -heartGUI.pixelInset.height;
AddHearts(startHealth/healthPerHeart);
}
public void AddHearts(int n) {
for (int i = 0; i <n; i ++) {
Transform newHeart = ((GameObject)Instantiate(heartGUI.gameObject,this.transform.position,Quaternion.identity)).transform; // Creates a new heart
newHeart.parent = transform;
int y = (int)(Mathf.FloorToInt(hearts.Count / maxHeartsOnRow));
int x = (int)(hearts.Count - y * maxHeartsOnRow);
newHeart.GetComponent<GUITexture>().pixelInset = new Rect(x * spacingX,y * spacingY,58,58);
newHeart.GetComponent<GUITexture>().texture = heartImages[0];
hearts.Add(newHeart);
}
maxHealth += n * healthPerHeart;
currentHealth = maxHealth;
UpdateHearts();
}
public void modifyHealth(int amount) {
currentHealth += amount;
currentHealth = Mathf.Clamp(currentHealth,0,maxHealth);
UpdateHearts();
}
void UpdateHearts() {
bool restAreEmpty = false;
int i =0;
foreach (Transform heart in hearts) {
if (restAreEmpty) {
heart.guiTexture.texture = heartImages[0]; // heart is empty
}
else {
i += 1; // current iteration
if (currentHealth >= i * healthPerHeart) {
heart.guiTexture.texture = heartImages[heartImages.Length-1]; // health of current heart is full
}
else {
int currentHeartHealth = (int)(healthPerHeart - (healthPerHeart * i - currentHealth));
int healthPerImage = healthPerHeart / heartImages.Length; // how much health is there per image
int imageIndex = currentHeartHealth / healthPerImage;
if (imageIndex == 0 && currentHeartHealth > 0) {
imageIndex = 1;
}
heart.guiTexture.texture = heartImages[imageIndex];
restAreEmpty = true;
}
}
}
}
}
my player script
/// <summary>
/// Player controller and behavior
/// </summary>
public class PlayerScript : MonoBehaviour
{
public Health health;
/// <summary>
/// 1 - The speed of the ship
/// </summary>
public Vector2 speed = new Vector2(50, 50);
// 2 - Store the movement
private Vector2 movement;
void OnCollisionEnter2D(Collision2D collision)
{
bool damagePlayer = false;
// Collision with enemy
EnemyScript enemy = collision.gameObject.GetComponent<EnemyScript>();
if (enemy != null)
{
// Kill the enemy
HealthScript enemyHealth = enemy.GetComponent<HealthScript>();
if (enemyHealth != null) enemyHealth.Damage(enemyHealth.hp);
damagePlayer = true;
}
// Damage the player
if (damagePlayer)
{
HealthScript playerHealth = this.GetComponent<HealthScript>();
if (playerHealth != null) playerHealth.Damage(1);
}
}
void Update()
{
// 3 - Retrieve axis information
float inputX = Input.GetAxis("Horizontal");
float inputY = Input.GetAxis("Vertical");
// 4 - Movement per direction
movement = new Vector2(
speed.x * inputX,
speed.y * inputY);
// 5 - Shooting
bool shoot = Input.GetButtonDown("Fire1");
shoot |= Input.GetButtonDown("Fire2");
// Careful: For Mac users, ctrl + arrow is a bad idea
if (shoot)
{
WeaponScript weapon = GetComponent<WeaponScript>();
if (weapon != null)
{
// false because the player is not an enemy
weapon.Attack(false);
}
}
// 6 - Make sure we are not outside the camera bounds
var dist = (transform.position - Camera.main.transform.position).z;
var leftBorder = Camera.main.ViewportToWorldPoint(
new Vector3(0, 0, dist)
).x;
var rightBorder = Camera.main.ViewportToWorldPoint(
new Vector3(1, 0, dist)
).x;
var topBorder = Camera.main.ViewportToWorldPoint(
new Vector3(0, 0, dist)
).y;
var bottomBorder = Camera.main.ViewportToWorldPoint(
new Vector3(0, 1, dist)
).y;
transform.position = new Vector3(
Mathf.Clamp(transform.position.x, leftBorder, rightBorder),
Mathf.Clamp(transform.position.y, topBorder, bottomBorder),
transform.position.z
);
}
void FixedUpdate()
{
// 5 - Move the game object
rigidbody2D.velocity = movement;
}
void OnDestroy()
{
Application.LoadLevel("gameOver");
}
}
and my player's health script
using UnityEngine;
/// <summary>
/// Handle hitpoints and damages
/// </summary>
public class HealthScript : MonoBehaviour
{
/// <summary>
/// Total hitpoints
/// </summary>
public int hp = 1;
/// <summary>
/// Enemy or player?
/// </summary>
public bool isEnemy = true;
/// <summary>
/// Inflicts damage and check if the object should be destroyed
/// </summary>
/// <param name="damageCount"></param>
public void Damage(int damageCount)
{
hp -= damageCount;
if (hp <= 0)
{
// 'Splosion!
SpecialEffectsHelper.Instance.Explosion(transform.position);
// Dead!
Destroy(gameObject);
}
}
void OnTriggerEnter2D(Collider2D otherCollider)
{
// Is this a shot?
ShotScript shot = otherCollider.gameObject.GetComponent<ShotScript>();
if (shot != null)
{
// Avoid friendly fire
if (shot.isEnemyShot != isEnemy)
{
Damage(shot.damage);
// Destroy the shot
Destroy(shot.gameObject); // Remember to always target the game object, otherwise you will just remove the script
}
}
}
}
With new UI system in Unity 4.6 it is really easy to create a health bar.
GameObject->UI->Image
Put your health bar sprite in image.
Change the Image type to Filled. Then you can play with Fill amount property and also control in through code
In your PlayerScript you retrieve the HealthScript with the following code:
HealthScript playerHealth = this.GetComponent<HealthScript>();
If you want to call methods on the Health script you would do something similar.
Health healthBar = this.GetComponent<Health>();
healthBar.modifyHealth(amountOfDamage);
This assumes all 3 scripts are on the same Game object.
I've got a ship that's spawning sprites. There are two types: railgun rounds and missiles. The missiles fire off properly, but the railgun rounds always spawn about one hundred pixels in front of the ship (or at least, it seems to). I'm not sure why this is happening. Also, there are two turrets, when it fires out of one, it's supposed to set a boolean value to true to make it fire out of the other one; when it fires out of that one, it sets the boolean back to false and so on. It always fires out of the first one. Again, no idea why. I've been banging my head against this all day. Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using ShipBattle.Classes.Ships;
using ShipBattle.Classes.Ships.Fighters;
using ShipBattle.Classes.Ships.Projectiles;
using ShipBattle.Classes.Ships.Projectiles.Tauri;
namespace ShipBattle.Classes.Ships.Fighters
{
public class F302 : Ship
{
const double missileMinimumFiringArc = Utilities.NINETY_DEGREES / 2;
const double missileMaximumFiringArc = Utilities.NINETY_DEGREES + (Utilities.NINETY_DEGREES / 2);
const double railgunMinimumFiringArc = 1.5;
const double railgunMaximumFiringArc = 1.6;
const double MISSILES_ROF = 30.0;
protected List<double> CooldownLeft = new List<double>();
private ContentManager content;
public event ShipFired WeaponFired;
public List<NaquadahEnhancedMissile> missiles;
public List<RailgunRound> railgunRounds;
public int[] MissileAmmo { get; set; }
public int[] RailgunAmmo { get; set; }
//Determines which missile/railgun to fire
bool leftMissile = true;
bool leftRailgun = true;
public F302(Vector2 position, ContentManager Content)
{
content = Content;
Texture = content.Load<Texture2D>(#"Textures\Ships\Tauri\Fighters\F302");
Position = position;
Rotation = 0;
#region Physics Stuff
mass = 19200;
force = 76.3f;
acceleration = (force * 1000) / mass;
maxSpeed = Utilities.GetVelocity(acceleration);
#endregion
#region Hull & Shielding
HullIntegrity = 10;
ShieldStrength = 0;
#endregion
#region Weapons!!!
/*
* [0] = Port Missile
* [1] = Starboard Missile
* [2] = Port Railgun
* [3] = Starboard Railgun
*/
//Setup
missiles = new List<NaquadahEnhancedMissile>();
railgunRounds = new List<RailgunRound>();
MissileAmmo = new int[2];
RailgunAmmo = new int[2];
//Port Missile
WeaponRatesOfFire.Add(MISSILES_ROF);
CooldownLeft.Add(0);
WeaponsEnplacements.Add(new Vector2(14, 5));
WeaponEmplacementOffsets.Add(new Vector2(14, 5));
MissileAmmo[0] = 2;
//Starboard Missile
WeaponRatesOfFire.Add(MISSILES_ROF);
CooldownLeft.Add(0);
WeaponsEnplacements.Add(new Vector2(35, 5));
WeaponEmplacementOffsets.Add(new Vector2(35, 5));
MissileAmmo[1] = 2;
//Port Railgun
WeaponRatesOfFire.Add(7.2);
CooldownLeft.Add(0);
WeaponsEnplacements.Add(new Vector2(24, 0));
WeaponEmplacementOffsets.Add(new Vector2(24, 0));
RailgunAmmo[0] = 10000;
//Starboard Railgun
WeaponRatesOfFire.Add(7.2);
CooldownLeft.Add(0);
WeaponsEnplacements.Add(new Vector2(26, 0));
WeaponEmplacementOffsets.Add(new Vector2(26, 0));
RailgunAmmo[1] = 10000;
#endregion
}
protected override void UpdateProjectiles(Vector2 pos)
{
for (int i = 0; i < missiles.Count; i++)
if (missiles[i].Remove)
missiles.RemoveAt(i);
for (int i = 0; i < railgunRounds.Count; i++)
if (railgunRounds[i].Remove)
railgunRounds.RemoveAt(i);
foreach (NaquadahEnhancedMissile nem in missiles)
nem.Update(enemyPos);
foreach (RailgunRound rr in railgunRounds)
rr.Update(enemyPos);
}
protected override void DrawProjectiles(SpriteBatch spriteBatch)
{
foreach (NaquadahEnhancedMissile nem in missiles)
nem.Draw(spriteBatch);
foreach (RailgunRound rr in railgunRounds)
rr.Draw(spriteBatch);
}
protected override void CheckTarget(Ship target)
{
enemyPos = target.Position;
double distance = Vector2.Distance(Position, target.Position);
Vector2 vector1 = Vector2.Normalize(Position - target.Position);
Vector2 vector2 = new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation));
double angle = Math.Acos(Vector2.Dot(vector1, vector2));
if (angle > missileMinimumFiringArc && angle < missileMaximumFiringArc)
if (distance < 500)
if (((target.HullIntegrity + target.ShieldStrength) - 10) <= 0)
FireMissiles();
if (angle > railgunMinimumFiringArc && angle < railgunMaximumFiringArc)
FireRailguns();
}
protected void FireMissiles()
{
if (leftMissile)
{
if (CooldownLeft[0] <= 0)
{
if (MissileAmmo[0] > 0)
{
NaquadahEnhancedMissile nem = new NaquadahEnhancedMissile(WeaponsEnplacements[0], Rotation, content);
nem.hit += new ProjectileHit(nem_hit);
missiles.Add(nem);
CooldownLeft[0] = WeaponRatesOfFire[0];
CooldownLeft[1] = WeaponRatesOfFire[1];
MissileAmmo[0]--;
leftMissile = false;
}
}
}
else
if (CooldownLeft[1] <= 0)
{
if (MissileAmmo[1] > 0)
{
NaquadahEnhancedMissile nem = new NaquadahEnhancedMissile(WeaponsEnplacements[1], Rotation, content);
nem.hit += new ProjectileHit(nem_hit);
missiles.Add(nem);
CooldownLeft[0] = WeaponRatesOfFire[0];
CooldownLeft[1] = WeaponRatesOfFire[1];
MissileAmmo[1]--;
leftMissile = true;
}
}
}
private void FireRailguns()
{
if (leftRailgun)
{
if (CooldownLeft[2] <= 0)
{
if (RailgunAmmo[0] > 0)
{
RailgunRound rgr = new RailgunRound(WeaponsEnplacements[2], Rotation, content);
rgr.hit += new ProjectileHit(nem_hit);
railgunRounds.Add(rgr);
CooldownLeft[2] = WeaponRatesOfFire[2];
CooldownLeft[3] = WeaponRatesOfFire[3];
RailgunAmmo[0]--;
leftRailgun = false;
}
}
}
else
if (CooldownLeft[3] <= 0)
{
if (RailgunAmmo[1] > 0)
{
RailgunRound rgr = new RailgunRound(WeaponsEnplacements[3], Rotation, content);
rgr.hit += new ProjectileHit(nem_hit);
railgunRounds.Add(rgr);
CooldownLeft[2] = WeaponRatesOfFire[2];
CooldownLeft[3] = WeaponRatesOfFire[3];
MissileAmmo[1]--;
leftRailgun = true;
}
}
}
protected override void Cooldown()
{
for (int f = 0; f < CooldownLeft.Count; f++)
CooldownLeft[f]--;
}
private void nem_hit(Projectile p, EventArgs e)
{
if (p is NaquadahEnhancedMissile)
{
p.Remove = true;
if (targetShip.ShieldStrength > 0)
targetShip.ShieldStrength -= p.Damage;
else
targetShip.HullIntegrity -= p.Damage;
}
else if (p is RailgunRound)
{
p.Remove = true;
if (targetShip.ShieldStrength > 0)
targetShip.ShieldStrength -= p.Damage / 4;
else
targetShip.HullIntegrity -= p.Damage;
}
}
protected override void CleanupProjectiles()
{
missiles.Clear();
railgunRounds.Clear();
}
}
}
And here's the code for the ship class this all inherits from:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ShipBattle.Classes.Ships.Projectiles;
using ShipBattle.Classes.Ships.Projectiles.Tauri;
namespace ShipBattle.Classes.Ships
{
public abstract class Ship
{
public Texture2D Texture { get; set; }
public Vector2 Position
{
get
{
return _position;
}
set
{
_position = value;
}
}
public float Rotation { get; set; }
private Vector2 _position;
protected Ship targetShip;
protected Vector2 enemyPos;
#region Health & Shielding
public float HullIntegrity { get; set; }
public float ShieldStrength { get; set; }
public bool Remove { get; set; }
#endregion
#region Guns
protected List<Vector2> WeaponsEnplacements = new List<Vector2>();
protected List<Vector2> WeaponEmplacementOffsets = new List<Vector2>();
protected List<double> WeaponRatesOfFire = new List<double>();
/// <summary>
/// The rates of fire for all weapons, represented in terms of the delay between frames
/// </summary>
#endregion
#region Targeting Logic
bool hasTarget = false;
protected int targetHashCode;
Vector2 targetShipPosition;
Ship target;
bool evasive = false;
bool hasRandomTrajectory = false;
bool reachedBounds = false;
bool followingRandom = false;
int timeToFaffAbout = 360;
double randomRotation;
#endregion
#region Physics Stuff
float angleA, b, a, speed = 0;
double turningRadius = 10 * (Math.PI / 180);
//Acceleration
protected int mass; // kg
protected float force; // kN, thruster power
protected float acceleration; // m/s^2
//Velocity
protected float maxSpeed; // m/s, calculated using 30-second burn
protected float initialSpeed = 0;
protected float finalSpeed = 0;
protected float time = 0.016666f;
#endregion
public void Update(List<Ship> ships)
{
if (timeToFaffAbout >= 0)
{
timeToFaffAbout = 360;
followingRandom = false;
}
if (!hasTarget)
{
targetShip = GetTarget(ships);
hasTarget = true;
}
else
{
if (targetShip != null)
{
if (Vector2.Distance(Position, targetShip.Position) < 75)
evasive = true;
else
evasive = false;
if (evasive)
{
if (!hasRandomTrajectory)
{
Random random = new Random();
randomRotation = random.Next((int)(Math.PI * 100));
double negative = random.Next(2);
if (negative == 1)
randomRotation *= -1;
Rotation = (float)randomRotation;
hasRandomTrajectory = true;
}
}
else
{
if (!followingRandom)
{
//Rotate the sprite using the turning radius
Rotation = Utilities.TurnToFace(Position, new Vector2(targetShip.Position.X, targetShip.Position.Y), (float)Rotation, (float)turningRadius);
}
}
KeepOnTheScreen();
//Move the sprite, using KINEMATIC PHYSICS, ***!!! -->goes in the direction set by the rotation algorithm
Move();
CheckTarget(targetShip);
UpdateProjectiles(targetShip.Position);
//Stop targeting a dead enemy
if (targetShip.HullIntegrity <= 0)
hasTarget = false;
}
}
//Recalculate the List<Vector2> weapons enplacements based on the current rotation angle
RecalculateWeaponsPositions();
//Cooldown the guns
Cooldown();
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, null, Color.White, Rotation, new Vector2(Texture.Width / 2, Texture.Height / 2), 0.5f,
SpriteEffects.None, 0.0f);
if (hasTarget)
DrawProjectiles(spriteBatch);
}
/// <summary>
/// Uses trig and the thruster power to move the ship. b is the y distance to move, a is the x distance to move
/// </summary>
private void Move()
{
if (finalSpeed < maxSpeed)
finalSpeed = speed + (acceleration * time);
angleA = Rotation;
b = (float)Math.Cos(angleA) * finalSpeed;
a = (float)Math.Sin(angleA) * finalSpeed;
_position.Y -= b;
_position.X += a;
speed = finalSpeed;
}
/// <summary>
/// Acquires the closes enemy ship
/// </summary>
/// <param name="ships">The ships to search through</param>
/// <returns></returns>
private Ship GetTarget(List<Ship> ships)
{
CleanupProjectiles();
Ship rVal = null;
int distance = int.MaxValue;
float c;
foreach (Ship ship in ships)
{
c = Vector2.Distance(Position, ship.Position);
if (c < distance)
rVal = ship;
}
return rVal;
}
/// <summary>
/// Reorients the positions of all the weapon positions on this ship
/// </summary>
private void RecalculateWeaponsPositions()
{
for (int i = 0; i < WeaponsEnplacements.Count; i++)
{
WeaponsEnplacements[i] = RotateWeapons(WeaponEmplacementOffsets[i]);
}
}
/// <summary>
/// Recalculates the positions of the weapons on this ship based off their default offset and the current angle
/// </summary>
/// <param name="weapon">The weapon position to recalculate</param>
/// <param name="offset">The default offset of that weapon</param>
private Vector2 RotateWeapons(Vector2 offset)
{
Quaternion rotation = Quaternion.CreateFromAxisAngle(Vector3.Backward, (float)Rotation);
return Vector2.Transform(offset, rotation) + Position;
}
/// <summary>
/// Keeps the ship on the screen
/// </summary>
private void KeepOnTheScreen()
{
if (Position.X > 1019 || Position.X < 5 || Position.Y > 761 || Position.Y < 5)
reachedBounds = true;
else
reachedBounds = false;
if (reachedBounds)
{
followingRandom = true;
if (!followingRandom)
Rotation = Utilities.TurnToFace(Position, new Vector2(1024 / 2, 768 / 2), Rotation, (float)turningRadius);
else
timeToFaffAbout--;
}
}
/// <summary>
/// Checks to see if the target ship is within weapons range
/// </summary>
/// <param name="target"></param>
protected abstract void CheckTarget(Ship target);
/// <summary>
/// Decrements the cooldown of all weapons
/// </summary>
protected abstract void Cooldown();
protected abstract void UpdateProjectiles(Vector2 pos);
protected abstract void DrawProjectiles(SpriteBatch spriteBatch);
protected abstract void CleanupProjectiles();
}
}
If anyone can help me with this, I'd be really appreciative. Thank you!
Why does FireRailguns() execute MissileAmmo[1]--; ?