shiftos-pong/WatercolorGames.Pong/Game1.cs
2018-03-12 12:02:33 -04:00

399 lines
16 KiB
C#

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Plex.Engine.GraphicsSubsystem;
using Microsoft.Xna.Framework.Input.Touch;
using System;
using Plex.Engine;
using Microsoft.Xna.Framework.Audio;
namespace WatercolorGames.Pong
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
private SpriteFont _scoreFont = null;
private SpriteFont _timeLeftFont = null;
private SpriteFont _bodyFont = null;
private double _timeLeftSeconds = 60;
private int _level = 1;
private long _codepoints = 0;
private SoundEffect _typesound = null;
private SoundEffect _writesound = null;
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
private float _playerPaddleY = 0.5F;
private float _cpuPaddleY = 0.5F;
private float _ballX = 0.5F;
private float _ballY = 0.5F;
private const float _paddleHeight = 0.15F;
private const float _paddleWidth = 0.05F;
private const float _ballSize = 0.1F;
private const float _ballSizeLandscape = 0.05F;
private Rectangle _ballRect = new Rectangle();
private Rectangle _playerRect = new Rectangle();
private Rectangle _cpuRect = new Rectangle();
private const long _cpLevelBeatReward = 1;
private const long _cpCpuBeatReward = 2;
private string _countdownHead = "Countdown header";
private string _countdownDesc = "Countdown description";
private double _countdown = 3;
private const float _paddleHeightLandscape = 0.20F;
private const float _paddleWidthLandscape = 0.025F;
private float _ballVelYStart = 0.0025F;
private float _ballVelX = 0.0025F;
private float _ballVelY = -0.0025F;
private float _cpuSpeed = 0.5F;
private int _gameState = 0;
private double _countdownBeepTimer = 0;
private bool _isLandscape = false;
private GraphicsContext _gfx = null;
private Activity1 _activity = null;
public Game1(Activity1 activity)
{
_activity = activity;
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
graphics.SupportedOrientations = DisplayOrientation.Portrait | DisplayOrientation.PortraitDown | DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
_gfx = new GraphicsContext(GraphicsDevice, spriteBatch, 0, 0, GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight);
_bodyFont = Content.Load<SpriteFont>("Fonts/MainBody");
_timeLeftFont = Content.Load<SpriteFont>("Fonts/TimeLeft");
_scoreFont = Content.Load<SpriteFont>("Fonts/Score");
_typesound = Content.Load<SoundEffect>("SFX/typesound");
_writesound = Content.Load<SoundEffect>("SFX/writesound");
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
private int _frames = 0;
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
Exit();
//In case the player rotates their screen...
_gfx.Width = GraphicsDevice.PresentationParameters.BackBufferWidth;
_gfx.Height = GraphicsDevice.PresentationParameters.BackBufferHeight;
//Now we need to figure out if we're landscape or not.
_isLandscape = GraphicsDevice.PresentationParameters.DisplayOrientation == DisplayOrientation.LandscapeLeft || GraphicsDevice.PresentationParameters.DisplayOrientation == DisplayOrientation.LandscapeLeft;
switch (_gameState)
{
case 0: //intro
foreach(var touch in TouchPanel.GetState())
{
if(touch.State == TouchLocationState.Released)
{
_countdownHead = "Ready to play?";
_countdownDesc = $"Level {_level} - you can earn:\n\n{_cpCpuBeatReward * _level} CP for beating the computer.\n{_cpLevelBeatReward * _level} CP for surviving.";
_gameState = 2;
break;
}
}
break;
case 1: //In-game.
_frames++;
TouchLocation previous = default(TouchLocation);
foreach (var touch in TouchPanel.GetState())
{
if (!touch.TryGetPreviousLocation(out previous))
continue;
var delta = previous.Position - touch.Position;
_playerPaddleY -= (delta.Y / _gfx.Width);
}
float ballSize = (_isLandscape) ? _ballSizeLandscape : _ballSize;
float paddleSize = (_isLandscape) ? _paddleWidthLandscape : _paddleWidth;
float paddleHeight = (_isLandscape) ? _paddleHeightLandscape : _paddleHeight;
_ballRect.Width = (int)Math.Round(MathHelper.Lerp(0, _gfx.Width, ballSize));
_ballRect.Height = (int)Math.Round(MathHelper.Lerp(0, _gfx.Height, ballSize));
_ballRect.X = (int)Math.Round(MathHelper.Lerp(0, _gfx.Width, _ballX - (ballSize / 2)));
_ballRect.Y = (int)MathHelper.Clamp((float)Math.Round(MathHelper.Lerp(0, _gfx.Height, _ballY - (ballSize / 2))), 0, _gfx.Height);
_playerRect.Width = (int)MathHelper.Lerp(0, _gfx.Width, paddleSize);
_playerRect.Height = (int)MathHelper.Lerp(0, _gfx.Height, paddleHeight);
_cpuRect.Width = _playerRect.Width;
_cpuRect.Height = _playerRect.Height;
float screenMarginX = 0.01f;
_playerRect.X = (int)MathHelper.Lerp(0, _gfx.Width, screenMarginX);
_cpuRect.X = (int)MathHelper.Lerp(0, _gfx.Width, (1F - screenMarginX) - paddleSize);
_playerRect.Y = (int)MathHelper.Lerp(0, _gfx.Height, _playerPaddleY - (paddleHeight / 2));
_cpuRect.Y = (int)MathHelper.Lerp(0, _gfx.Height, _cpuPaddleY - (paddleHeight / 2));
if (_ballRect.Top <= 0)
{
_ballY = ballSize / 2;
_ballVelY = -_ballVelY;
}
if (_ballRect.Bottom >= _gfx.Height)
{
_ballY = 1f - (ballSize / 2F);
_ballVelY = -_ballVelY;
}
if (_cpuRect.Intersects(_ballRect))
{
float ballInPaddle = _ballY - (_cpuPaddleY - (paddleHeight / 2));
float hitLocationPercentage = ballInPaddle / paddleHeight;
_ballVelX = -_ballVelX;
_ballVelY = MathHelper.Lerp(-_ballVelYStart, _ballVelYStart, hitLocationPercentage);
_typesound.Play();
}
if (_playerRect.Intersects(_ballRect))
{
float ballInPaddle = _ballY - (_playerPaddleY - (paddleHeight / 2));
float hitLocationPercentage = ballInPaddle / paddleHeight;
_ballX = screenMarginX + paddleSize + (ballSize / 2);
_ballVelX = -_ballVelX;
_ballVelY = MathHelper.Lerp(-_ballVelYStart, _ballVelYStart, hitLocationPercentage);
_typesound.Play();
}
if (_ballRect.Left >= _gfx.Width)
{
_ballX = 0.85F;
_ballY = 0.5F;
_codepoints += (_cpCpuBeatReward * _level);
_ballVelX = -_ballVelX;
_countdownHead = "You Beat the Computer!";
_countdownDesc = $"{_cpCpuBeatReward * _level} Codepoints rewarded.";
_gameState++;
}
if (_ballRect.Right <= 0)
{
_ballX = 0.5F;
_ballY = 0.5F;
_ballVelX = 0.0025F;
_ballVelY = 0.0025F;
_ballVelYStart = 0.0025F;
_playerPaddleY = 0.5F;
_cpuPaddleY = 0.5F;
_countdownHead = "You Lost!";
_countdownDesc = $"You missed out on {_codepoints} Codepoints!";
_codepoints = 0;
_timeLeftSeconds = 60;
_level = 1;
_gameState++;
return;
}
_ballX += _ballVelX;
_ballY += _ballVelY;
_timeLeftSeconds -= gameTime.ElapsedGameTime.TotalSeconds;
if(_timeLeftSeconds < 0)
{
_timeLeftSeconds = 60;
_ballVelYStart *= 2;
_ballVelX *= 2;
_codepoints += _cpLevelBeatReward * _level;
_countdownHead = $"Level {_level} Complete";
_level++;
_countdownDesc = $"Now on Level {_level} - you can earn:\n\n{_cpCpuBeatReward * _level} CP for beating the computer.\n{_cpLevelBeatReward * _level} CP for surviving.";
_gameState++;
}
if (_frames >= 7)
{
_frames = 0;
return;
}
if (_cpuPaddleY > _ballY)
{
_cpuPaddleY -= (_ballVelYStart * _cpuSpeed);
}
else if (_cpuPaddleY < _ballY)
{
_cpuPaddleY += (_ballVelYStart * _cpuSpeed);
}
_playerPaddleY = MathHelper.Clamp(_playerPaddleY, paddleHeight / 2, 1 - (paddleHeight / 2));
break;
case 2: //Countdown.
_countdownBeepTimer += gameTime.ElapsedGameTime.TotalSeconds;
if(_countdownBeepTimer>=1)
{
_writesound.Play();
_countdownBeepTimer = 0;
}
_countdown -= gameTime.ElapsedGameTime.TotalSeconds;
if(_countdown < 0)
{
_gameState--;
_countdown = 3;
}
break;
}
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
_gfx.Device.Clear(Color.Black);
_gfx.BeginDraw();
switch (_gameState)
{
case 0:
string welcome = _activity.ApplicationContext.Resources.GetString(Resource.String.WelcomeToPong);
var measure = TextRenderer.MeasureText(welcome, _bodyFont, (int)(_gfx.Width / 1.25), WrapMode.Words);
_gfx.DrawString(welcome, (_gfx.Width - (int)(_gfx.Width / 1.25)) / 2, (_gfx.Height - (int)measure.Y) / 2, Color.White, _bodyFont, TextAlignment.Center, (int)(_gfx.Width / 1.25), WrapMode.Words);
break;
case 1:
//Draw the two paddles.
_gfx.DrawRectangle(_playerRect.X, _playerRect.Y, _playerRect.Width, _playerRect.Height, Color.White);
_gfx.DrawRectangle(_cpuRect.X, _cpuRect.Y, _cpuRect.Width, _cpuRect.Height, Color.White);
//Draw the ball.
_gfx.DrawCircle(_ballRect.X + (_ballRect.Width / 2), _ballRect.Y + (_ballRect.Width / 2), _ballRect.Width / 2, Color.White);
break;
case 2:
var headMeasure = TextRenderer.MeasureText(_countdownHead, _bodyFont, (int)(_gfx.Width / 1.25), WrapMode.Words);
_gfx.DrawString(_countdownHead, (_gfx.Width - (int)(_gfx.Width / 1.25)) / 2, _gfx.Height / 4, Color.White, _bodyFont, TextAlignment.Center, (int)(_gfx.Width / 1.25), WrapMode.Words);
_gfx.DrawString(_countdownDesc, (_gfx.Width - (_gfx.Width/2)) / 2, (_gfx.Height / 4) + (int)headMeasure.Y + 30, Color.White, _scoreFont, TextAlignment.Center, _gfx.Width/2, WrapMode.Words);
string countdownText = Math.Round(_countdown).ToString();
var countdownMeasure = _timeLeftFont.MeasureString(countdownText);
_gfx.Batch.DrawString(_timeLeftFont, countdownText, new Vector2((_gfx.Width - countdownMeasure.X) / 2, (_gfx.Height - countdownMeasure.Y) / 2), Color.White);
break;
}
//Measure the "Seconds Left" counter.
string secondsleft = $"{Math.Round(_timeLeftSeconds)} Seconds Left";
//Render the seconds left counter
_gfx.DrawString(secondsleft, (_gfx.Width - (_gfx.Width / 2)) / 2, 20, Color.White, _timeLeftFont, TextAlignment.Center, _gfx.Width / 2, WrapMode.Words);
//Level text
string level = $"Level: {_level}";
var lMeasure = TextRenderer.MeasureText(level, _scoreFont, 0, WrapMode.None);
//render level text
_gfx.DrawString(level, 20, (_gfx.Height - (int)lMeasure.Y) - 20, Color.White, _scoreFont, TextAlignment.Left, 0, WrapMode.None);
//Codepoints text
string codepoints = $"{_codepoints} Codepoints";
var cMeasure = TextRenderer.MeasureText(codepoints, _scoreFont, 0, WrapMode.None);
//render codepoints text
_gfx.DrawString(codepoints, (_gfx.Width - (int)cMeasure.X)-20, (_gfx.Height - (int)lMeasure.Y) - 20, Color.White, _scoreFont, TextAlignment.Left, 0, WrapMode.None);
_gfx.EndDraw();
base.Draw(gameTime);
}
}
}