Add project files.
This commit is contained in:
51
AlientAttack.MonoGame/Things/Bullets/Bullet.cs
Normal file
51
AlientAttack.MonoGame/Things/Bullets/Bullet.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things.Bullets;
|
||||
|
||||
internal class Bullet(float x, float y, float xVel, float yVel, Sprite owner) : Sprite(x, y)
|
||||
{
|
||||
protected float XVelocity = xVel;
|
||||
protected float YVelocity = yVel;
|
||||
public Sprite Owner { get; protected set; } = owner;
|
||||
public int Damage { get; protected set; } = 0;
|
||||
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
base.Draw(args);
|
||||
}
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
XPosition += XVelocity;
|
||||
YPosition += YVelocity;
|
||||
|
||||
if (XPosition + BoundBox.Width < 0
|
||||
|| XPosition > context.ViewTransform.ScreenWidth
|
||||
|| YPosition + BoundBox.Height < 0
|
||||
|| YPosition > context.ViewTransform.ScreenHeight)
|
||||
{
|
||||
IsDead = true;
|
||||
}
|
||||
|
||||
base.Update(context);
|
||||
}
|
||||
|
||||
public override void OnCollision(SpriteCollisionContext context)
|
||||
{
|
||||
if (context.Sprite is Bullet || context.Sprite == Owner || context.Sprite is Item)
|
||||
return;
|
||||
|
||||
IsDead = true;
|
||||
|
||||
float xVel = 0;
|
||||
float yVel = 0;
|
||||
|
||||
if (context.Sprite is MoveableSprite moveableSprite)
|
||||
{
|
||||
xVel = moveableSprite.XVelocity;
|
||||
yVel = moveableSprite.YVelocity;
|
||||
}
|
||||
|
||||
context.SpawnSprite(new MiniExplosion((int)XPosition, (int)YPosition, xVel, yVel));
|
||||
}
|
||||
}
|
||||
26
AlientAttack.MonoGame/Things/Bullets/MinigunBulletSmall.cs
Normal file
26
AlientAttack.MonoGame/Things/Bullets/MinigunBulletSmall.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things.Bullets;
|
||||
|
||||
internal class MinigunBulletSmall : Bullet
|
||||
{
|
||||
public MinigunBulletSmall(float x, float y, float xVel, float yVel, Sprite owner) : base(x, y, xVel, yVel, owner)
|
||||
{
|
||||
XVelocity = xVel;
|
||||
YVelocity = yVel;
|
||||
Owner = owner;
|
||||
Damage = 1;
|
||||
}
|
||||
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\Minigun_Small");
|
||||
|
||||
float rotation = MathF.Atan2(YVelocity, XVelocity) + MathF.PI / 2f;
|
||||
Vector2 origin = new(texture.Width / 2f, texture.Height / 2f);
|
||||
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, rotation, origin, 1f, SpriteEffects.None, 1);
|
||||
}
|
||||
}
|
||||
117
AlientAttack.MonoGame/Things/Enemies/Enemy.cs
Normal file
117
AlientAttack.MonoGame/Things/Enemies/Enemy.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using AlienAttack.MonoGame.Things.Bullets;
|
||||
using AlienAttack.MonoGame.Things.Items;
|
||||
using AlienAttack.MonoGame.Things.Weapons;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things.Enemies;
|
||||
|
||||
internal class Enemy : MoveableSprite
|
||||
{
|
||||
//Enemy01_Green_Frame_1_png_processed
|
||||
|
||||
protected ICollection<IWeapon> ActiveWeapons = [];
|
||||
protected int FireThreshold => 20;
|
||||
protected int CurrentFireThreshold { get; set; } = 20;
|
||||
protected int Health { get; set; } = 5;
|
||||
|
||||
public Enemy(int x, int y) : base(x, y)
|
||||
{
|
||||
BoundBox = new Rectangle(0, 0, 64, 64);
|
||||
YVelocity = 1;
|
||||
ActiveWeapons.Add(new Minigun());
|
||||
//ActiveWeapons.Add(new FastMinigun());
|
||||
}
|
||||
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\Enemy01_Green_Frame_1_png_processed");
|
||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), 1, spriteEffects, 1);
|
||||
|
||||
base.Draw(args);
|
||||
}
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
//YPosition += 1;
|
||||
|
||||
if (Health <= 0)
|
||||
{
|
||||
IsDead = true;
|
||||
context.SpawnSprite(new Explosion((int)XPosition, (int)YPosition, XVelocity, YVelocity));
|
||||
|
||||
switch (context.Random.Next(0, 4))
|
||||
{
|
||||
case 0:
|
||||
context.SpawnSprite(new Health((int)XPosition, (int)YPosition));
|
||||
break;
|
||||
case 1:
|
||||
context.SpawnSprite(new Shields((int)XPosition, (int)YPosition));
|
||||
break;
|
||||
case 2:
|
||||
context.SpawnSprite(new Ammo((int)XPosition, (int)YPosition));
|
||||
break;
|
||||
case 3:
|
||||
context.SpawnSprite(new Energy((int)XPosition, (int)YPosition));
|
||||
break;
|
||||
case 4:
|
||||
context.SpawnSprite(new Rockets((int)XPosition, (int)YPosition));
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (YPosition > context.ViewTransform.ScreenHeight)
|
||||
{
|
||||
IsDead = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurrentFireThreshold > 0)
|
||||
{
|
||||
CurrentFireThreshold--;
|
||||
}
|
||||
|
||||
if (CurrentFireThreshold == 0 && context.Random.Next(0, 100) == 1)
|
||||
{
|
||||
float originX = XPosition + (BoundBox.Width / 2) - (7 / 2);
|
||||
|
||||
context.SpawnSprite(new MinigunBulletSmall(originX - 9, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this));
|
||||
context.SpawnSprite(new MinigunBulletSmall(originX + 14, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this));
|
||||
|
||||
CurrentFireThreshold = FireThreshold;
|
||||
}
|
||||
|
||||
//CheckMove(context);
|
||||
CheckFire(context);
|
||||
|
||||
base.Update(context);
|
||||
}
|
||||
|
||||
private void CheckFire(SpriteUpdateContext context)
|
||||
{
|
||||
//foreach (IWeapon weapon in ActiveWeapons)
|
||||
//{
|
||||
// weapon.UpdateFireThreshold();
|
||||
//}
|
||||
|
||||
//foreach (IWeapon weapon in ActiveWeapons)
|
||||
//{
|
||||
// weapon.TryFire(this, context);
|
||||
//}
|
||||
}
|
||||
|
||||
public override void OnCollision(SpriteCollisionContext context)
|
||||
{
|
||||
if (context.Sprite is Bullet bullet && bullet.Owner is Player)
|
||||
{
|
||||
Health -= bullet.Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
76
AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs
Normal file
76
AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using AlienAttack.MonoGame.Things.Bullets;
|
||||
using AlienAttack.MonoGame.Things.Weapons;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things.Enemies;
|
||||
|
||||
internal class RedEnemy : MoveableSprite
|
||||
{
|
||||
//Enemy01_Green_Frame_1_png_processed
|
||||
|
||||
protected int Health { get; set; } = 10;
|
||||
|
||||
public RedEnemy(int x, int y) : base(x, y)
|
||||
{
|
||||
BoundBox = new Rectangle(0, 0, 64, 64);
|
||||
YVelocity = 2;
|
||||
//ActiveWeapons.Add(new Minigun());
|
||||
//ActiveWeapons.Add(new FastMinigun());
|
||||
}
|
||||
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\Enemy01_Red_Frame_1_png_processed");
|
||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), 1, spriteEffects, 1);
|
||||
|
||||
base.Draw(args);
|
||||
}
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
//YPosition += 2;
|
||||
|
||||
if (Health <= 0)
|
||||
{
|
||||
IsDead = true;
|
||||
context.SpawnSprite(new Explosion((int)XPosition, (int)YPosition, XVelocity, YVelocity));
|
||||
return;
|
||||
}
|
||||
|
||||
if (YPosition > context.ViewTransform.ScreenHeight)
|
||||
{
|
||||
IsDead = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//CheckMove(context);
|
||||
CheckFire(context);
|
||||
|
||||
base.Update(context);
|
||||
}
|
||||
|
||||
private void CheckFire(SpriteUpdateContext context)
|
||||
{
|
||||
//foreach (IWeapon weapon in ActiveWeapons)
|
||||
//{
|
||||
// weapon.UpdateFireThreshold();
|
||||
//}
|
||||
|
||||
//foreach (IWeapon weapon in ActiveWeapons)
|
||||
//{
|
||||
// weapon.TryFire(this, context);
|
||||
//}
|
||||
}
|
||||
|
||||
public override void OnCollision(SpriteCollisionContext context)
|
||||
{
|
||||
if (context.Sprite is Bullet bullet && bullet.Owner is Player)
|
||||
{
|
||||
Health -= bullet.Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
AlientAttack.MonoGame/Things/Explosion.cs
Normal file
44
AlientAttack.MonoGame/Things/Explosion.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things;
|
||||
|
||||
internal class Explosion(int x, int y, float xVel, float yVel) : Sprite(x, y)
|
||||
{
|
||||
protected int CurrentFrame { get; private set; } = 1;
|
||||
protected int MaxFrames => 9;
|
||||
protected int AnimationThreshold => 3; //5;
|
||||
protected int CurrentThreshold { get; private set; } = 5;
|
||||
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\Explosion01_Frame_0{CurrentFrame}_png_processed");
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), 1f, SpriteEffects.None, 1);
|
||||
|
||||
base.Draw(args);
|
||||
}
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
base.Update(context);
|
||||
|
||||
XPosition += xVel;
|
||||
YPosition += yVel;
|
||||
|
||||
if (CurrentThreshold > 0)
|
||||
{
|
||||
CurrentThreshold--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CurrentFrame == MaxFrames)
|
||||
{
|
||||
IsDead = true;
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentFrame++;
|
||||
CurrentThreshold = AnimationThreshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
AlientAttack.MonoGame/Things/Items/Ammo.cs
Normal file
14
AlientAttack.MonoGame/Things/Items/Ammo.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
internal class Ammo : Item
|
||||
{
|
||||
public Ammo(int x, int y) : base(x, y)
|
||||
{
|
||||
TextureName = @$"Sprites\Powerup_Ammo";
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(Player player)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
14
AlientAttack.MonoGame/Things/Items/Energy.cs
Normal file
14
AlientAttack.MonoGame/Things/Items/Energy.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
internal class Energy : Item
|
||||
{
|
||||
public Energy(int x, int y) : base(x, y)
|
||||
{
|
||||
TextureName = @$"Sprites\Powerup_Energy";
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(Player player)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
14
AlientAttack.MonoGame/Things/Items/Health.cs
Normal file
14
AlientAttack.MonoGame/Things/Items/Health.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
internal class Health : Item
|
||||
{
|
||||
public Health(int x, int y) : base(x, y)
|
||||
{
|
||||
TextureName = @$"Sprites\Powerup_Health";
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(Player player)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
68
AlientAttack.MonoGame/Things/Items/Item.cs
Normal file
68
AlientAttack.MonoGame/Things/Items/Item.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
internal abstract class Item : MoveableSprite
|
||||
{
|
||||
private Vector2 _anchor; // the "center" the item orbits around
|
||||
private float _t; // radians
|
||||
private float _radius = 6f; // pixels (small circle)
|
||||
private float _omega = 2.2f; // radians/sec (speed of orbit)
|
||||
private float _scale = 1f;
|
||||
|
||||
protected string? TextureName;
|
||||
|
||||
public Item(int x, int y) : base(x, y)
|
||||
{
|
||||
YVelocity = .5f;
|
||||
BoundBox = new Rectangle(0, 0, 48, 29);
|
||||
|
||||
_anchor = new Vector2(x, y);
|
||||
}
|
||||
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
if (TextureName is null)
|
||||
return;
|
||||
|
||||
Texture2D texture = args.Content.Load<Texture2D>(TextureName);
|
||||
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), _scale, SpriteEffects.None, 1);
|
||||
}
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
// Move the anchor using your normal velocities (downward drift)
|
||||
_anchor.Y += YVelocity;
|
||||
_anchor.X += XVelocity;
|
||||
|
||||
// Advance time smoothly (use dt; if you don't have it yet, see note below)
|
||||
float dt = (float)context.GameTime.ElapsedGameTime.TotalSeconds;
|
||||
_t += _omega * dt;
|
||||
|
||||
// Apply circular offset around the anchor
|
||||
XPosition = _anchor.X + MathF.Cos(_t) * _radius;
|
||||
YPosition = _anchor.Y + MathF.Sin(_t) * _radius;
|
||||
|
||||
if (YPosition > context.ViewTransform.ScreenHeight)
|
||||
{
|
||||
IsDead = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.Update(context);
|
||||
}
|
||||
|
||||
public override void OnCollision(SpriteCollisionContext context)
|
||||
{
|
||||
if (context.Sprite is Player player)
|
||||
{
|
||||
IsDead = true;
|
||||
ApplyEffect(player);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void ApplyEffect(Player player);
|
||||
}
|
||||
14
AlientAttack.MonoGame/Things/Items/Rockets.cs
Normal file
14
AlientAttack.MonoGame/Things/Items/Rockets.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
internal class Rockets : Item
|
||||
{
|
||||
public Rockets(int x, int y) : base(x, y)
|
||||
{
|
||||
TextureName = @$"Sprites\Powerup_Rockets";
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(Player player)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
14
AlientAttack.MonoGame/Things/Items/Shields.cs
Normal file
14
AlientAttack.MonoGame/Things/Items/Shields.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
internal class Shields : Item
|
||||
{
|
||||
public Shields(int x, int y) : base(x, y)
|
||||
{
|
||||
TextureName = @$"Sprites\Powerup_Shields";
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(Player player)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
44
AlientAttack.MonoGame/Things/MiniExplosion.cs
Normal file
44
AlientAttack.MonoGame/Things/MiniExplosion.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things;
|
||||
|
||||
internal class MiniExplosion(int x, int y, float xVel, float yVel) : Sprite(x, y)
|
||||
{
|
||||
protected int CurrentFrame { get; private set; } = 1;
|
||||
protected int MaxFrames => 9;
|
||||
protected int AnimationThreshold => 3; //5;
|
||||
protected int CurrentThreshold { get; private set; } = 5;
|
||||
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\Explosion01_Frame_0{CurrentFrame}_png_processed");
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), .25f, SpriteEffects.None, 1);
|
||||
|
||||
base.Draw(args);
|
||||
}
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
base.Update(context);
|
||||
|
||||
XPosition += xVel;
|
||||
YPosition += yVel;
|
||||
|
||||
if (CurrentThreshold > 0)
|
||||
{
|
||||
CurrentThreshold--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CurrentFrame == MaxFrames)
|
||||
{
|
||||
IsDead = true;
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentFrame++;
|
||||
CurrentThreshold = AnimationThreshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
AlientAttack.MonoGame/Things/MoveableSprite.cs
Normal file
15
AlientAttack.MonoGame/Things/MoveableSprite.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace AlienAttack.MonoGame.Things;
|
||||
|
||||
public class MoveableSprite(int x, int y) : Sprite(x, y)
|
||||
{
|
||||
public float XVelocity { get; protected set; } = 0;
|
||||
public float YVelocity { get; protected set; } = 0;
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
XPosition += XVelocity;
|
||||
YPosition += YVelocity;
|
||||
|
||||
base.Update(context);
|
||||
}
|
||||
}
|
||||
272
AlientAttack.MonoGame/Things/Player.cs
Normal file
272
AlientAttack.MonoGame/Things/Player.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using AlienAttack.MonoGame.Things.Weapons;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things;
|
||||
|
||||
internal enum PlayerHorizontalMoveState
|
||||
{
|
||||
None,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum MoveFlag
|
||||
{
|
||||
None = 0,
|
||||
Left = 1,
|
||||
Right = 2,
|
||||
Up = 4,
|
||||
Down = 8
|
||||
}
|
||||
|
||||
public class DrwaState
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal class Player : MoveableSprite
|
||||
{
|
||||
protected PlayerHorizontalMoveState MoveState = PlayerHorizontalMoveState.None;
|
||||
protected MoveFlag MoveFlags;
|
||||
protected ulong MoveThreshold = 0;
|
||||
//protected ulong FireThreshold = 0;
|
||||
protected ICollection<IWeapon> ActiveWeapons = [];
|
||||
|
||||
protected int CurrentExhaustFrame = 1;
|
||||
protected int MaxExhaustFrames = 6;
|
||||
protected int ExhaustAnimationThreshold = 30;
|
||||
protected int CurrentExhaustAnimationThreshold = 10;
|
||||
protected int CurrentExhaustDirection = 1;
|
||||
|
||||
public Player(int x, int y) : base(x, y)
|
||||
{
|
||||
BoundBox = new Rectangle(0, 0, 64, 64);
|
||||
ActiveWeapons.Add(new Minigun());
|
||||
ActiveWeapons.Add(new FastMinigun());
|
||||
}
|
||||
|
||||
//Texture2D texture = Game
|
||||
public override void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
//DrawExhaust(args);
|
||||
|
||||
string frameNumber = MoveState == PlayerHorizontalMoveState.None ? "01" :
|
||||
MoveThreshold < 30 ? "02" : "03";
|
||||
SpriteEffects spriteEffects = MoveState == PlayerHorizontalMoveState.Right ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
|
||||
|
||||
Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\PlayerRed_Frame_{frameNumber}");
|
||||
//args.SpriteBatch.Draw(texture, Position, DrawColor);
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), 1, spriteEffects, 1);
|
||||
}
|
||||
|
||||
private void DrawExhaust(SpriteDrawArgs args)
|
||||
{
|
||||
Texture2D texture = args.Content.Load<Texture2D>(@$"Sprites\Exhaust_Frame_0{CurrentExhaustFrame}_png_processed");
|
||||
args.SpriteBatch.Draw(texture, Position, null, DrawColor, 0, new Vector2(0, 0), 1, SpriteEffects.None, 1);
|
||||
}
|
||||
|
||||
private string GetPlayerTextureName()
|
||||
{
|
||||
switch (MoveState)
|
||||
{
|
||||
case PlayerHorizontalMoveState.Left:
|
||||
return "PlayerRed_Frame_02";
|
||||
case PlayerHorizontalMoveState.Right:
|
||||
return "PlayerRed_Frame_03";
|
||||
case PlayerHorizontalMoveState.None:
|
||||
default:
|
||||
return "PlayerRed_Frame_01";
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(SpriteUpdateContext context)
|
||||
{
|
||||
//UpdateExhaustAnimationThreshold();
|
||||
CheckMove(context);
|
||||
CheckFire(context);
|
||||
|
||||
base.Update(context);
|
||||
}
|
||||
|
||||
private void UpdateExhaustAnimationThreshold()
|
||||
{
|
||||
if (CurrentExhaustAnimationThreshold > 0)
|
||||
{
|
||||
CurrentExhaustAnimationThreshold--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CurrentExhaustFrame == MaxExhaustFrames)
|
||||
{
|
||||
CurrentExhaustDirection = -1;
|
||||
}
|
||||
else if (CurrentExhaustFrame == 1)
|
||||
{
|
||||
CurrentExhaustDirection = 1;
|
||||
}
|
||||
|
||||
CurrentExhaustFrame += CurrentExhaustDirection;
|
||||
|
||||
CurrentExhaustAnimationThreshold = ExhaustAnimationThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckMove(SpriteUpdateContext context)
|
||||
{
|
||||
UpdateMoveFlag();
|
||||
|
||||
if (MoveFlags.HasFlag(MoveFlag.Up))
|
||||
{
|
||||
//YPosition -= 4;
|
||||
YVelocity = -4;
|
||||
}
|
||||
else if (MoveFlags.HasFlag(MoveFlag.Down))
|
||||
{
|
||||
//YPosition += 4;
|
||||
YVelocity = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
YVelocity = 0;
|
||||
}
|
||||
|
||||
if (MoveFlags.HasFlag(MoveFlag.Left))
|
||||
{
|
||||
//XPosition -= 4;
|
||||
XVelocity = -4;
|
||||
|
||||
if (MoveState != PlayerHorizontalMoveState.Left)
|
||||
{
|
||||
MoveThreshold = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveThreshold++;
|
||||
|
||||
if (MoveThreshold > 30)
|
||||
{
|
||||
MoveThreshold = 30;
|
||||
}
|
||||
}
|
||||
|
||||
MoveState = PlayerHorizontalMoveState.Left;
|
||||
}
|
||||
else if (MoveFlags.HasFlag(MoveFlag.Right))
|
||||
{
|
||||
//XPosition += 4;
|
||||
XVelocity = 4;
|
||||
|
||||
if (MoveState != PlayerHorizontalMoveState.Right)
|
||||
{
|
||||
MoveThreshold = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveThreshold++;
|
||||
|
||||
if (MoveThreshold > 30)
|
||||
{
|
||||
MoveThreshold = 30;
|
||||
}
|
||||
}
|
||||
|
||||
MoveState = PlayerHorizontalMoveState.Right;
|
||||
}
|
||||
else
|
||||
{
|
||||
XVelocity = 0;
|
||||
MoveState = PlayerHorizontalMoveState.None;
|
||||
MoveThreshold = 0;
|
||||
}
|
||||
|
||||
if (XPosition < 0)
|
||||
{
|
||||
XPosition = 0;
|
||||
}
|
||||
|
||||
if (XPosition + BoundBox.Width > context.ViewTransform.ScreenWidth)
|
||||
{
|
||||
XPosition = context.ViewTransform.ScreenWidth - BoundBox.Width;
|
||||
}
|
||||
|
||||
if (YPosition < 0)
|
||||
{
|
||||
YPosition = 0;
|
||||
}
|
||||
|
||||
if (YPosition + BoundBox.Height > context.ViewTransform.ScreenHeight)
|
||||
{
|
||||
YPosition = context.ViewTransform.ScreenHeight - BoundBox.Height;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMoveFlag()
|
||||
{
|
||||
MoveFlags = MoveFlag.None;
|
||||
|
||||
KeyboardState keyState = Keyboard.GetState();
|
||||
GamePadState gamepadState = GamePad.GetState(0);
|
||||
|
||||
if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W) || gamepadState.ThumbSticks.Left.Y > 0)
|
||||
{
|
||||
MoveFlags |= MoveFlag.Up;
|
||||
}
|
||||
else if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S) || gamepadState.ThumbSticks.Left.Y < 0)
|
||||
{
|
||||
MoveFlags |= MoveFlag.Down;
|
||||
}
|
||||
|
||||
if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A) || gamepadState.ThumbSticks.Left.X < 0)
|
||||
{
|
||||
MoveFlags |= MoveFlag.Left;
|
||||
}
|
||||
else if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D) || gamepadState.ThumbSticks.Left.X > 0)
|
||||
{
|
||||
MoveFlags |= MoveFlag.Right;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckFire(SpriteUpdateContext context)
|
||||
{
|
||||
//if (FireThreshold > 0)
|
||||
//{
|
||||
// FireThreshold--;
|
||||
// return;
|
||||
//}
|
||||
|
||||
foreach (IWeapon weapon in ActiveWeapons)
|
||||
{
|
||||
weapon.UpdateFireThreshold();
|
||||
}
|
||||
|
||||
if (IsFireButtonPressed() == false)
|
||||
return;
|
||||
|
||||
foreach (IWeapon weapon in ActiveWeapons)
|
||||
{
|
||||
weapon.TryFire(this, context);
|
||||
}
|
||||
|
||||
//MinigunBulletSmall bullet1 = new((int)Position.X + 12, (int)Position.Y + 6, 0, -4, this);
|
||||
//MinigunBulletSmall bullet2 = new((int)Position.X + BoundBox.Width - 20, (int)Position.Y + 6, 0, -4, this);
|
||||
|
||||
//context.SpawnSprite(bullet1);
|
||||
//context.SpawnSprite(bullet2);
|
||||
|
||||
//FireThreshold = 15;
|
||||
}
|
||||
|
||||
private static bool IsFireButtonPressed()
|
||||
{
|
||||
KeyboardState keyState = Keyboard.GetState();
|
||||
MouseState mouseState = Mouse.GetState();
|
||||
GamePadState gamePadState = GamePad.GetState(0);
|
||||
|
||||
return keyState.IsKeyDown(Keys.Space) || mouseState.LeftButton == ButtonState.Pressed || gamePadState.Buttons.A == ButtonState.Pressed;
|
||||
}
|
||||
}
|
||||
38
AlientAttack.MonoGame/Things/Sprite.cs
Normal file
38
AlientAttack.MonoGame/Things/Sprite.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things;
|
||||
|
||||
public class Sprite(float x, float y)
|
||||
{
|
||||
public float XPosition { get; protected set; } = x;
|
||||
public float YPosition { get; protected set; } = y;
|
||||
public Vector2 Position => new(XPosition, YPosition);
|
||||
public Rectangle BoundBox { get; protected set; }
|
||||
|
||||
protected Rectangle CollisionBox;
|
||||
protected Color DrawColor = Color.White;
|
||||
|
||||
public bool CanCollide { get; protected set; } = true;
|
||||
public bool IsDead { get; protected set; }
|
||||
|
||||
public virtual void Update(SpriteUpdateContext context)
|
||||
{
|
||||
CollisionBox = new Rectangle((int)XPosition + BoundBox.X, (int)YPosition + BoundBox.Y, BoundBox.Width, BoundBox.Height);
|
||||
}
|
||||
|
||||
public bool Intersects(Sprite sprite)
|
||||
{
|
||||
return CollisionBox.Intersects(sprite.CollisionBox);
|
||||
}
|
||||
|
||||
public virtual void Draw(SpriteDrawArgs args)
|
||||
{
|
||||
//spriteBatch.Draw(Texture, Position, DrawColor);
|
||||
}
|
||||
|
||||
public virtual void OnCollision(SpriteCollisionContext context)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
AlientAttack.MonoGame/Things/SpriteDrawArgs.cs
Normal file
11
AlientAttack.MonoGame/Things/SpriteDrawArgs.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things;
|
||||
|
||||
public class SpriteDrawArgs(AlienAttackGame game)
|
||||
{
|
||||
public SpriteBatch SpriteBatch => game.SpriteBatch;
|
||||
public ContentManager Content => game.Content;
|
||||
}
|
||||
20
AlientAttack.MonoGame/Things/SpriteUpdateContext.cs
Normal file
20
AlientAttack.MonoGame/Things/SpriteUpdateContext.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using AlienAttack.MonoGame.View;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things;
|
||||
|
||||
public class SpriteUpdateContext(AlienAttackGame game)
|
||||
{
|
||||
public ViewTransform ViewTransform => game.ViewTransform;
|
||||
public required Action<Sprite> SpawnSprite { get; init; }
|
||||
public required Random Random { get; init; }
|
||||
public required GameTime GameTime { get; init; }
|
||||
}
|
||||
|
||||
public class SpriteCollisionContext(AlienAttackGame game)
|
||||
{
|
||||
public ViewTransform ViewTransform => game.ViewTransform;
|
||||
public required Sprite Sprite { get; init; }
|
||||
public required Action<Sprite> SpawnSprite { get; init; }
|
||||
}
|
||||
190
AlientAttack.MonoGame/Things/Stars/Starfield.cs
Normal file
190
AlientAttack.MonoGame/Things/Stars/Starfield.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things.Stars
|
||||
{
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public sealed class Starfield
|
||||
{
|
||||
private readonly List<StarLayer> _layers = new();
|
||||
private readonly Random _rng;
|
||||
|
||||
public Starfield(int seed = 12345) => _rng = new Random(seed);
|
||||
|
||||
public void AddLayer(int count, Vector2 velocityPxPerSec, float minSize, float maxSize, float minAlpha, float maxAlpha)
|
||||
{
|
||||
_layers.Add(new StarLayer(_rng, count, velocityPxPerSec, minSize, maxSize, minAlpha, maxAlpha));
|
||||
}
|
||||
|
||||
public void Initialize(int screenWidth, int screenHeight)
|
||||
{
|
||||
foreach (var layer in _layers)
|
||||
layer.Initialize(screenWidth, screenHeight);
|
||||
}
|
||||
|
||||
public void OnResize(int screenWidth, int screenHeight)
|
||||
{
|
||||
foreach (var layer in _layers)
|
||||
layer.OnResize(screenWidth, screenHeight);
|
||||
}
|
||||
|
||||
public void Update(GameTime gameTime)
|
||||
{
|
||||
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
|
||||
foreach (var layer in _layers)
|
||||
layer.Update(dt);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Texture2D pixel)
|
||||
{
|
||||
foreach (var layer in _layers)
|
||||
layer.Draw(spriteBatch, pixel);
|
||||
}
|
||||
|
||||
private sealed class StarLayer
|
||||
{
|
||||
private readonly Random _rng;
|
||||
private readonly int _count;
|
||||
//private readonly float _speed;
|
||||
private readonly Vector2 _velocity; // pixels/sec
|
||||
private readonly float _minSize, _maxSize;
|
||||
private readonly float _minAlpha, _maxAlpha;
|
||||
|
||||
private int _w, _h;
|
||||
private readonly Star[] _stars;
|
||||
|
||||
public StarLayer(Random rng, int count, Vector2 velocityPxPerSec, float minSize, float maxSize, float minAlpha, float maxAlpha)
|
||||
{
|
||||
_rng = rng;
|
||||
_count = count;
|
||||
//_speed = speedPxPerSec;
|
||||
_velocity = velocityPxPerSec;
|
||||
_minSize = minSize;
|
||||
_maxSize = maxSize;
|
||||
_minAlpha = minAlpha;
|
||||
_maxAlpha = maxAlpha;
|
||||
|
||||
_stars = new Star[_count];
|
||||
}
|
||||
|
||||
public void Initialize(int screenWidth, int screenHeight)
|
||||
{
|
||||
_w = screenWidth;
|
||||
_h = screenHeight;
|
||||
|
||||
for (int i = 0; i < _stars.Length; i++)
|
||||
_stars[i] = CreateStar(randomY: true);
|
||||
}
|
||||
|
||||
public void OnResize(int screenWidth, int screenHeight)
|
||||
{
|
||||
// Keep existing stars but clamp/wrap them into the new bounds.
|
||||
_w = screenWidth;
|
||||
_h = screenHeight;
|
||||
|
||||
for (int i = 0; i < _stars.Length; i++)
|
||||
{
|
||||
var s = _stars[i];
|
||||
s.Pos.X = Wrap(s.Pos.X, _w);
|
||||
s.Pos.Y = Wrap(s.Pos.Y, _h);
|
||||
_stars[i] = s;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float dt)
|
||||
{
|
||||
for (int i = 0; i < _stars.Length; i++)
|
||||
{
|
||||
var s = _stars[i];
|
||||
|
||||
// Move down (positive Y). Add slight horizontal drift if you want:
|
||||
// s.Pos.X += s.DriftX * dt;
|
||||
|
||||
//s.Pos.Y += _speed * dt;
|
||||
s.Pos += _velocity * dt;
|
||||
|
||||
// Wrap X continuously so diagonal drift never runs out of stars
|
||||
if (s.Pos.X < -s.Size) s.Pos.X = _w + s.Size;
|
||||
if (s.Pos.X > _w + s.Size) s.Pos.X = -s.Size;
|
||||
|
||||
if (s.Pos.Y >= _h + s.Size)
|
||||
{
|
||||
// Respawn at top; keep it entering from above
|
||||
s = CreateStar(randomY: false);
|
||||
s.Pos.Y = -s.Size;
|
||||
}
|
||||
|
||||
// Optional subtle twinkle:
|
||||
s.TwinkleT += dt * s.TwinkleSpeed;
|
||||
|
||||
_stars[i] = s;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch sb, Texture2D pixel)
|
||||
{
|
||||
for (int i = 0; i < _stars.Length; i++)
|
||||
{
|
||||
var s = _stars[i];
|
||||
|
||||
float twinkle = 1f;
|
||||
if (s.TwinkleSpeed > 0f)
|
||||
twinkle = 0.85f + 0.15f * (float)Math.Sin(s.TwinkleT);
|
||||
|
||||
var color = Color.White * (s.Alpha * twinkle);
|
||||
|
||||
// Draw as a scaled 1x1 pixel (super fast)
|
||||
sb.Draw(pixel, s.Pos, null, color, 0f, Vector2.Zero, s.Size, SpriteEffects.None, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
private Star CreateStar(bool randomY)
|
||||
{
|
||||
float x = (float)_rng.NextDouble() * _w;
|
||||
float y = randomY ? (float)_rng.NextDouble() * _h : 0f;
|
||||
|
||||
float size = Lerp(_minSize, _maxSize, (float)_rng.NextDouble());
|
||||
float alpha = Lerp(_minAlpha, _maxAlpha, (float)_rng.NextDouble());
|
||||
|
||||
// Twinkle: keep it more common on “near” layers by setting a nonzero speed range
|
||||
float twinkleSpeed = Lerp(0f, 6f, (float)_rng.NextDouble()) * 0.4f; // tweak or set 0 for none
|
||||
|
||||
return new Star
|
||||
{
|
||||
Pos = new Vector2(x, y),
|
||||
Size = size,
|
||||
Alpha = alpha,
|
||||
TwinkleSpeed = twinkleSpeed,
|
||||
TwinkleT = (float)_rng.NextDouble() * MathF.PI * 2f
|
||||
};
|
||||
}
|
||||
|
||||
private static float Wrap(float v, float max)
|
||||
{
|
||||
if (max <= 0) return 0;
|
||||
v %= max;
|
||||
if (v < 0) v += max;
|
||||
return v;
|
||||
}
|
||||
|
||||
private static float Lerp(float a, float b, float t) => a + (b - a) * t;
|
||||
|
||||
private struct Star
|
||||
{
|
||||
public Vector2 Pos;
|
||||
public float Size;
|
||||
public float Alpha;
|
||||
|
||||
public float TwinkleSpeed;
|
||||
public float TwinkleT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
AlientAttack.MonoGame/Things/Weapons/IWeapon.cs
Normal file
8
AlientAttack.MonoGame/Things/Weapons/IWeapon.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace AlienAttack.MonoGame.Things.Weapons;
|
||||
|
||||
public interface IWeapon
|
||||
{
|
||||
int FireThreshold { get; }
|
||||
void UpdateFireThreshold();
|
||||
bool TryFire(Sprite owner, SpriteUpdateContext context);
|
||||
}
|
||||
43
AlientAttack.MonoGame/Things/Weapons/Minigun.cs
Normal file
43
AlientAttack.MonoGame/Things/Weapons/Minigun.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using AlienAttack.MonoGame.Things.Bullets;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace AlienAttack.MonoGame.Things.Weapons;
|
||||
|
||||
public class Minigun : Weapon
|
||||
{
|
||||
public override int FireThreshold => 15;
|
||||
|
||||
public override void Fire(Sprite owner, SpriteUpdateContext context)
|
||||
{
|
||||
// Calculate bullet spawn positions relative to the player's bounding box
|
||||
int x1 = (int)owner.XPosition + 12;
|
||||
int x2 = (int)owner.XPosition + owner.BoundBox.Width - 20;
|
||||
int y = (int)owner.YPosition + 6;
|
||||
|
||||
// Create bullets with velocity (0, -4)
|
||||
MinigunBulletSmall bullet1 = new(x1, y, 0, -6, owner);
|
||||
MinigunBulletSmall bullet2 = new(x2, y, 0, -6, owner);
|
||||
|
||||
// Queue the bullets for spawning
|
||||
context.SpawnSprite(bullet1);
|
||||
context.SpawnSprite(bullet2);
|
||||
}
|
||||
}
|
||||
|
||||
public class FastMinigun : Weapon
|
||||
{
|
||||
public override int FireThreshold => 10;
|
||||
|
||||
public override void Fire(Sprite owner, SpriteUpdateContext context)
|
||||
{
|
||||
// Calculate bullet spawn positions relative to the player's bounding box
|
||||
float x = (owner.Position.X - (7/2) + owner.BoundBox.Width / 2);
|
||||
float y = owner.YPosition - 16;
|
||||
|
||||
// Create bullets with velocity (0, -4)
|
||||
MinigunBulletSmall bullet = new(x, y, 0, -6, owner);
|
||||
|
||||
// Queue the bullets for spawning
|
||||
context.SpawnSprite(bullet);
|
||||
}
|
||||
}
|
||||
26
AlientAttack.MonoGame/Things/Weapons/Weapon.cs
Normal file
26
AlientAttack.MonoGame/Things/Weapons/Weapon.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace AlienAttack.MonoGame.Things.Weapons;
|
||||
|
||||
public abstract class Weapon : IWeapon
|
||||
{
|
||||
public abstract int FireThreshold { get; }
|
||||
public int CurrentFireThreshold { get; private set; }
|
||||
|
||||
public void UpdateFireThreshold()
|
||||
{
|
||||
if (CurrentFireThreshold > 0)
|
||||
CurrentFireThreshold--;
|
||||
}
|
||||
|
||||
public bool TryFire(Sprite owner, SpriteUpdateContext context)
|
||||
{
|
||||
if (CurrentFireThreshold > 0)
|
||||
return false;
|
||||
|
||||
Fire(owner, context);
|
||||
CurrentFireThreshold = FireThreshold;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract void Fire(Sprite owner, SpriteUpdateContext context);
|
||||
}
|
||||
Reference in New Issue
Block a user