• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
  • entries
    11
  • comments
    15
  • views
    16195

About this blog

Reinventing Old Games (for practice)

Entries in this blog

Josip Mati?

Project update

Hello

So, classes started again, and my duties as a student returned. Luckily, my schedule is pretty neat (monday and friday full, wednesday normal, thursday labs here and there, and tuesday empty), so I can pretty much arrange everything to perform at my maximum capacity. Classes I'm attending to are Math 2, Physics 1, Network Programming, Development of Software Applications and Engineering Economics.

So, with that in mind, I've been working last few weeks improving the game from my speedrun and I got it to the pretty satisfying state, requiring only few bugfixes here and there to call it mechanically "complete".

screenshot9.jpg

There are three current issues in the game:

  1. While ramming into moving solid tiles (walls, switches), ball is capable of becoming stuck into them. In certain situations, player can move the ball into a non-moving solid tile, becoming pernamently stuck. Players also can get stuck when standing into the place of the appearing wall (I have 2 options here - push ball out of the way or kill player. probably both depending if there's free space around or not).
  2. Text scripts aren't implemented properly.
  3. Background.

For background, I used plasma fractal algorithm to generate it. While it isn't visible from screenshot and requires


or demo to show it, constantly exchanging background can irritate eyes.

What I aimed for is effect similar to this, but the best I can is shown on video. I don't have any idea how to animate even grayscale to make something decent, nor could I find anything while doing research - all my search showed me the algorithm I already implemented.

My next step is to rewrite standalone level editor, which is horrible in the current state. Unlike the game itself, level editor is based on the Windows Form and uses elements like PictureBox, Buttons and others. While the editor... kinda... looks decent:

110en89.jpg

code behind is unmaintainable and full of bugs.
For example, when I save a fresh level, then load it again, this happens:

2zr1i07.jpg

The thing is, I don't have any idea why... that... happens.. kill me, I just realized while writing this - when creating an array of tiles (enum), array elements are by default first initialized to 0, which is enum value of gray ball.

So, I've decided to rewrite level editor to use XNA and behaves like a game, trading one set of problems for another. XNA doesn't have controls like windows forms have, so I'll have to write similar things myself. I also hope it will end up visually more appealing, and one day I might even integrate it into game itself as game is close to the end of its lifespan. If I ever get to that point.

Back to the game itself, I'm currently looking to upgrade sprites and music - either by doing them myself, or to find someone willing to do it instead of me, as I suck in both. Maybe even change the background.
Also, Softpedia put both on my games on their page, only notifying me after that. While I was initially suprised, I'm glad they did that.

Plan for the game is this:

  1. Finish it completely for Windows using XNA and polish it as much as possible.
  2. Convert it to Monogame and release Linux and Mac friendly version
  3. Given enough interest, either manually rewrite it into Java for Android and iOS or pay access to Xamarin to be able to simply port it with Monogame.

Thanks for reading!
Josip Mati?
Phew, it's been a while.

Semester finally over, all classes have been passed, now I have free time until summer semester starts full force. Not that I've had to do much anyway as classes were easier...

Anyway. This will be like a personal diary, so my rambling might be incoherent.

Speedrun



Yesterday, after exam passed, I came up on the idea of performing a programming speedrun, in order to see how fast, using tools I know, can I spit out working prototype of the game.
For game, I choose a simple gamplay: a ball which moves vertically at a constant speed, chaging direction when it hits an obstacle. Player navigates through the labyrinth using left and right arrow to control its horizontal movement and has to come to the exit while avoiding hazards.

So, armed with C#, XNA and Photoshop, I started at exactly 0:00 CET from complete scratch. 2 hours later, after basic code was written with some hardcoding of values, I've started to get something which looks like the game I had in mind:
104n9yr.jpg

At that moment, only level loading, ball vertical movement and vertical reflection were implemented. Code was a mess even at that point, but I didn't stop and refactor, but continued writing for 1 hour and 40 minutes, finishing the game with horizontal movement, hazard detection, camera movement and exit point:

https://www.dropbox.com/s/glbl4b9y5dmrfnm/BouncyBall.rar

(requires .NET 4.0 and XNA Redistributable)



So, in total, 3 hours and 40 minutes (pauses included) were taken for a complete game. And now, I'm in dilemma: what now to do with it? I've got so many questions for myself which I'm unable to answer. I could polish it in few days of pure work, but I don't know if it's worth it. Biggest issue with the game itself is rectangle-rectangle collision between ball and objects, which I left in the game to be faster and should be fixed as it is fake difficulty. Other basic additions are main menu, proper level progression, level editor and multiple obstacles and hazards. Code itself is also a complete and utter mess which should be sliced apart with refactor knife...

Screenshot showing how insane levels can be...
2a4tmht.jpg

About other topics...



Tank City is still on hold until I raise enough will to finish it. I plan to do it before summer semester starts.

As a player of Star Trek Online and member of one fleet there, I've teamed up with one other programmer for a "fleet project". We've started work on our "Ship Build planner" to incorporate with our fleet site and, if possible, compete with STO Academy's Build planner. I hope I'll learn more about web developing, databases etc. from that.

On the artistic side, I've finally properly took pen and pencil and started drawing. After copying few images only by eye, I've set out to draw characters from my mind with as little references as possible. Results were... well... satisfying for me, considering those are 6th and 7th drawing:
wklcte.jpg

I need to learn to properly draw eyes (big eyes aren't problem, but when I need to draw them small...) and hands. I hope I'll learn to use sister's tablet and be able to redraw them properly in digital format...

Thanks for reading!
Josip Mati?
Hello

While it's on my mind, I wanted to write here few lessons I learned in last few days.

  1. If the language supports polymorphism, USE IT.
  2. Design before coding.
  3. Having properly structured code helps. A LOT.
  4. Keep the objects closely related on one heap.

Those points don't seem to be related, right? Allow me to explain.

I've started to hunt coop related bugs in the game. Code, on the first look, seems to work perfectly; however, most code is structured like:if (multiPlayer == 0){ // do single player behavior}else{ // do co op behavior}
or a variation of that code, depending on player which "activates" part of the code.

So, here's lesson 1: Why making numerous comparisons like that through the whole class - some of which are surprisingly volatile - when I can make new class which inherits the current one and override specific methods to adapt them for co-op mode of the game. With that, I'm also avoiding the problem of pointless allocation of resources (time and memory) for object related to co-op mode.

Sound simple, right? Well, lessons 2 and 3 strike here at the same time. Due to "designing on the fly", I have to rewrite and refactor big parts of the code to adapt them for polymorphism. This means a lot of wasted time, due to the though process:

"Hmm, I'd like to add . Well, I can add this code here, and that code there... *writing code* Oh cool, this works! Yaay!"
"Now, to add ... *writing* Crap, code isn't good here anymore... *moving and/or rewriting code* Good, now works."
"OK, is next... oh not again, is broken. *rewriting and/or moving code*"
"ARRRRRGH, NOT AGAIN!" (I'm now at this point)

Result: lots of wasted time trying to improve the structure of the game while trying to preserve current functionality (a.k.a. trying to NOT break it).

However, while I'm rewriting the code, I have to be careful. Some parts of the code, with best example being Draw(GameTime) method where last drawn object is on top, require executing in specific order. So, I can't simply do this:public class A : GameScreen{ // ... public override void Draw(GameTime gameTime) { // ... }}public class B : A{ // ... public override void Draw(GameTime gameTime) { // ... spriteBatch.Draw(player2texture, player2rectangle, Color.White); base.Draw(gameTime); }}
because Player 2's sprite would end on bottom of everything, which may not be desirable.

Solution for that problem that I came up with is to extract relevant parts of A.Draw() and raise them to Protected level:public class A : GameScreen{ // ... public override void Draw(GameTime gameTime) { PartA(); PartB(); } protected void PartA() { // ... } protected void PartB() { // ... }}public class B : A{ // ... public override void Draw(GameTime gameTime) { PartA(); spriteBatch.Draw(player2texture, player2rectangle, Color.White); PartB(); }}
This is also why properly structured code is necessary. Instead of having massive amount of code inside one method, having the same code split over several methods improves maintainability and readability.

Lesson 4 is connected to 2 and 3 as well. Inside my Gameplay class (the one having actual game logic) I had few... awkward? objects:Texture2D player1Texture;byte player1spawnID;Texture2D player2Texture;byte player2spawnID;
Why is that?Player player1;Player player2;public override void LoadContent(){ player1 = ScreenManager.Player1; player2 = ScreenManager.Player2;}
In other words, I have objects related to players, whose objects persist through whole game, being created, disposed and recreated inside the class having game logic. Memory problems aside, not having player sprites stored inside player classes also forces me to needlessly duplicate the code and use additional checks to decide whose texture I need to move. Luckily, I don't need to do much to restructure the code and eliminate that annoyance.

Suddenly, improving level fie doesn't seem so important.

So, what did I learn from all that for next project (which is already defined in my head)?

I wasted too much time restructuring current project to enable modifications and upgrades. In order to avoid this in next project, I need to write Game Design Document and Tech Document in order to precisely define project and save time.

I hope that I won't repeat mistakes in the next project (Snake clone with 3D camera.)

After reading some topics on GameDev.Net, especially this one, I realized I have to learn the following:

  • Garbage collector
  • Making unit tests

    Thanks for reading, I'm returning to my code. smile.png
Josip Mati?

Salty update.

Hello.

Greeting from the sunny (and disgustingly windy) beach in Croatia. It's good to get away from the regular style of life; for some reasons my productivity arises while on vacation. Only problem is the lack of the normal connection to the internet, but I've managed to snatch dad's mobile internet USB stick so I can get here on occasions, so, this journal entry will be short.

Currently, I'm trying to establish the rhythm:

  • Breakfast @ approx 9:15
  • Beach
  • Math study
  • Lunch
  • Gamedev
  • Beach
  • Physics study
  • Dinner
  • Gaming
  • Sleep at 23:00-24:00

    For now, I'm more or less abiding to it. I hope I'll manage to prepare myself before next semester.

    Coding update



    All the major features are finished!
    I'm ironing out the bugs and finishing Options screen - only setting controls ingame is now required to implement.

    Aside from that, after looking through the code, I started refactoring it. Smart decision when Update method holds 50% of relevant game logic. Only problem is organizing the code; despite refactoring, the most important class still feels clunky. But readable.

    Artistic update



    Nothing special on this front. Sadly, the workspace here isn't suitable for practicing art... I miss my 32' TV screen and Razer Copperhead.
    I'm trying some combinations for improving the HUD and message boxes.

    Miscellaneous



    After some talking with jbadams, I said "it's enough" content-wise and decided I'll try and sell the game once polishing is finished - at least to see and learn about marketing/distribution/PR/etc. Otherwise, I'll never stop adding new, possibly questionable, content which can be added later anyway to keep the game fresh and thus never release the game. While I don't have much time to research about that field - at least until my Math and Physics exams finish, hopefully with passing grade - I put my game on IndieDB, where I'll also update status as soon as new material to update with appears.
    Thanks for reading!
Josip Mati?
I've been working a little on my old laptop. After digging through My Documents in search for some old documents, I've noticed VS2010 folder in it. With my curiosity suddenly awakened, I suddenly found myself staring at the content of the Projects folder.

Reason: some of my old projects were there. Most of them were games. A collection of 1s and 0s, silently waiting for their creator to come back for them. And he came.

After copying all the projects to the PC I work on, I loaded each solution and, after performing some maintenance like retargeting compiler to compile using .NET 3.0, ran debugger for each of them.
And felt like I was back there, looking at younger me sitting in front of PC I had at that time and trying to cope up with a solution to the problem.

So, here there are, the "games" I made before. Please join me in the travel to the past.
Note: I'll post code of each project. Because it's going to be huge, I'll put it in spoilers so that you can skip it if you're not interested.

Year 2006: Duck Shooting



2hgfsbc.jpg

First game I ever made, using Visual Basic (.NET Framework 2.0) with XNA 3.1. I have no idea how long it took me to make it. Gameplay is simple: shoot ducks and dog using the mouse to score points. Blue ducks are the slowest and give least amount of points; green are in the middle and brown are fastest. Dog jumps at random times and gives the most points.

Maybe you, dear reader, recognize the ducks and dog. Yeah, they're from Duck Hunt. And yeah, I hated that dog so much I created a way to get my revenge on him for mocking me each time I missed those damned duck(s). However, game is satisfying to play for maximum 60 seconds.

As for the code... when I look it now, it is atrocious. However, when I calmed myself I realized that... I didn't know better at that time. That was my best. For those who love statistics and code analysis, here's the data Code Metrics gave:

  • Maintanability Index: 59
  • Cyclomatic Complexity: 52
  • Depth of Inheritance: 2
  • Class Coupling: 32
  • Lines of Code: 243

    Code for itself is rather easy to follow. However, I violated so many principles that it might be a bit painful to read.

    XNAGame class:
    [spoiler]Imports Microsoft.Xna.FrameworkImports Microsoft.Xna.Framework.GraphicsImports Microsoft.Xna.Framework.InputNamespace xna Public Class XNAGame Inherits Game Public Shared XNAGraphics As GraphicsDeviceManager Public Sub New() XNAGraphics = New GraphicsDeviceManager(Me) End Sub Protected Overrides Sub Initialize() MyBase.IsMouseVisible = True Randomize() XNAGraphics.PreferredBackBufferWidth = 800 XNAGraphics.PreferredBackBufferHeight = 600 XNAGraphics.IsFullScreen = False XNAGraphics.ApplyChanges() InitializeTextures() MyBase.Initialize() End Sub Protected Overrides Sub Update(ByVal gameTime As Microsoft.Xna.Framework.GameTime) Dim GetKeys As KeyboardState = Keyboard.GetState Dim GetMouse As MouseState = Mouse.GetState If GetKeys.IsKeyDown(Keys.Escape) Then MyBase.Exit() End If If ButtonState.Pressed = GetMouse.LeftButton And Pressed = False Then Pressed = True Detect(GetMouse.X, GetMouse.Y) End If If ButtonState.Released = GetMouse.LeftButton Then Pressed = False End If Me.Window.Title = "Duck Shooting " + Score.ToString MyBase.Update(gameTime) Application.DoEvents() End Sub Protected Overrides Sub Draw(ByVal gameTime As Microsoft.Xna.Framework.GameTime) DuckGame.DrawMain() MyBase.Draw(gameTime) End Sub End Class Module modMain Public Game1 As XNAGame Public Sub Main() Game1 = New XNAGame Game1.Run() End Sub End ModuleEnd Namespace
    [/spoiler]
    DuckGame module:
    [spoiler]Imports Microsoft.Xna.FrameworkImports Microsoft.Xna.Framework.GraphicsNamespace xna Module DuckGame Public Sub DrawMain() XNAGame.XNAGraphics.GraphicsDevice.Clear(ClearOptions.Target, Color.DeepSkyBlue, 1.0F, 0) Rand = Rnd() If (Rand < 0.002) And (DogJump = False) Then DogJump = True If DogJump = True Then DogSB.Begin() If x <= 400 Then DogRect1.X = x DogRect1.Y = Math.Pow(x - 400, 2) / 80 + 100 DogSB.Draw(DogTexture1, DogRect1, Color.White) Else DogRect2.X = x DogRect2.Y = Math.Pow(x - 400, 2) / 80 + 100 DogSB.Draw(DogTexture2, DogRect2, Color.White) If x = 600 Then x = 200 DogJump = False End If End If x += 3 DogSB.End() End If DuckSB.Begin() RBlueDuck.UpdateFrame() DuckSB.Draw(RBlueDuck.ShowFrame, RBlueDuckRect, Color.White) If RBlueDuckRect.X < xna.Game1.Window.ClientBounds.Right Then RBlueDuckRect.X += 2 Else RBlueDuckRect.X = -80 RBlueDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If LBlueDuck.UpdateFrame() DuckSB.Draw(LBlueDuck.ShowFrame, LBlueDuckRect, Color.White) If LBlueDuckRect.X > -80 Then LBlueDuckRect.X -= 2 Else LBlueDuckRect.X = xna.Game1.Window.ClientBounds.Width LBlueDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If RGreenDuck.UpdateFrame() DuckSB.Draw(RGreenDuck.ShowFrame, RGreenDuckRect, Color.White) If RGreenDuckRect.X < xna.Game1.Window.ClientBounds.Right Then RGreenDuckRect.X += 4 Else RGreenDuckRect.X = -80 RGreenDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If LGreenDuck.UpdateFrame() DuckSB.Draw(LGreenDuck.ShowFrame, LGreenDuckRect, Color.White) If LGreenDuckRect.X > -80 Then LGreenDuckRect.X -= 4 Else LGreenDuckRect.X = xna.Game1.Window.ClientBounds.Width LGreenDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If RRedDuck.UpdateFrame() DuckSB.Draw(RRedDuck.ShowFrame, RRedDuckRect, Color.White) If RRedDuckRect.X < xna.Game1.Window.ClientBounds.Right Then RRedDuckRect.X += 6 Else RRedDuckRect.X = -80 RRedDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If LRedDuck.UpdateFrame() DuckSB.Draw(LRedDuck.ShowFrame, LRedDuckRect, Color.White) If LRedDuckRect.X > -80 Then LRedDuckRect.X -= 6 Else LRedDuckRect.X = xna.Game1.Window.ClientBounds.Width LRedDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If DuckSB.End() BackgroundSB.Begin() BackgroundSB.Draw(BackgroundTexture, BackgroundRect, Color.White) BackgroundSB.End() End Sub End ModuleEnd Namespace
    [/spoiler]
    DefineTextures module:
    [spoiler]Imports Microsoft.Xna.FrameworkImports Microsoft.Xna.Framework.GraphicsNamespace xna Module DefineTextures Public Device As GraphicsDevice = XNAGame.XNAGraphics.GraphicsDevice 'Background Setup Public BackgroundSB As SpriteBatch = Nothing Public BackgroundRect As Rectangle = Nothing Public BackgroundTexture As Texture2D = Nothing Public BackgroundCreationParams As TextureCreationParameters = TextureCreationParameters.Default 'Dog Setup Public DogSB As SpriteBatch = Nothing Public DogRect1 As Rectangle = Nothing Public DogRect2 As Rectangle = Nothing Public DogTexture1 As Texture2D = Nothing Public DogTexture2 As Texture2D = Nothing Public DogCreationParams As TextureCreationParameters = TextureCreationParameters.Default Public DogJump As Boolean = False 'Duck Setup Public DuckSB As SpriteBatch = Nothing Public DuckCreationParams As TextureCreationParameters = TextureCreationParameters.Default Public RBlueDuck As New AnimatedTexture Public LBlueDuck As New AnimatedTexture Public RGreenDuck As New AnimatedTexture Public LGreenDuck As New AnimatedTexture Public RRedDuck As New AnimatedTexture Public LRedDuck As New AnimatedTexture Public RBlueDuckRect As Rectangle = Nothing Public LBlueDuckRect As Rectangle = Nothing Public RGreenDuckRect As Rectangle = Nothing Public LGreenDuckRect As Rectangle = Nothing Public RRedDuckRect As Rectangle = Nothing Public LRedDuckRect As Rectangle = Nothing 'Time and Score Public Score As Integer = 0 Public ScoreText As SpriteFont Public Time As Stopwatch = New Stopwatch Public TimeText As String 'Other Public GameState As String = "Menu" Public x As Integer = 200 Public Rand As Single Public Pressed As Boolean = False Public Sub InitializeTextures() Background() Dog() DuckSB = New SpriteBatch(Device) Ducks() End Sub Private Sub Background() BackgroundTexture = Texture2D.FromFile(Device, "../../../Images/Background.png") BackgroundRect.Width = xna.Game1.Window.ClientBounds.Width BackgroundRect.Height = xna.Game1.Window.ClientBounds.Height BackgroundSB = New SpriteBatch(Device) BackgroundRect.X = 0 BackgroundRect.Y = 0 End Sub Private Sub Dog() DogSB = New SpriteBatch(Device) DogTexture1 = Texture2D.FromFile(Device, "../../../Images/Dog-Up.png") DogTexture2 = Texture2D.FromFile(Device, "../../../Images/Dog-Down.png") DogRect1.Width = DogTexture1.Width * 2 DogRect1.Height = DogTexture1.Height * 2 DogRect2.Width = DogTexture2.Width * 2 DogRect2.Height = DogTexture2.Height * 2 End Sub Private Sub Ducks() RBlueDuck.Load("../../../Images/R-Blue-Duck-1.png", 1, 30) RBlueDuck.Load("../../../Images/R-Blue-Duck-2.png", 2, 30) RBlueDuck.Load("../../../Images/R-Blue-Duck-3.png", 3, 30) RBlueDuck.UpdateFrame() RBlueDuckRect.X = -80 RBlueDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 RBlueDuckRect.Width = RBlueDuck.ShowFrame.Width RBlueDuckRect.Height = RBlueDuck.ShowFrame.Height LBlueDuck.Load("../../../Images/L-Blue-Duck-1.png", 1, 30) LBlueDuck.Load("../../../Images/L-Blue-Duck-2.png", 2, 30) LBlueDuck.Load("../../../Images/L-Blue-Duck-3.png", 3, 30) LBlueDuck.UpdateFrame() LBlueDuckRect.X = xna.Game1.Window.ClientBounds.Width LBlueDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 LBlueDuckRect.Width = LBlueDuck.ShowFrame.Width LBlueDuckRect.Height = LBlueDuck.ShowFrame.Height RGreenDuck.Load("../../../Images/R-Green-Duck-1.png", 1, 20) RGreenDuck.Load("../../../Images/R-Green-Duck-2.png", 2, 20) RGreenDuck.Load("../../../Images/R-Green-Duck-3.png", 3, 20) RGreenDuck.UpdateFrame() RGreenDuckRect.X = -80 RGreenDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 RGreenDuckRect.Width = RGreenDuck.ShowFrame.Width RGreenDuckRect.Height = RGreenDuck.ShowFrame.Height LGreenDuck.Load("../../../Images/L-Green-Duck-1.png", 1, 20) LGreenDuck.Load("../../../Images/L-Green-Duck-2.png", 2, 20) LGreenDuck.Load("../../../Images/L-Green-Duck-3.png", 3, 20) LGreenDuck.UpdateFrame() LGreenDuckRect.X = xna.Game1.Window.ClientBounds.Width LGreenDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 LGreenDuckRect.Width = LGreenDuck.ShowFrame.Width LGreenDuckRect.Height = LGreenDuck.ShowFrame.Height RRedDuck.Load("../../../Images/R-Red-Duck-1.png", 1, 10) RRedDuck.Load("../../../Images/R-Red-Duck-2.png", 2, 10) RRedDuck.Load("../../../Images/R-Red-Duck-3.png", 3, 10) RRedDuck.UpdateFrame() RRedDuckRect.X = -80 RRedDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 RRedDuckRect.Width = RRedDuck.ShowFrame.Width RRedDuckRect.Height = RRedDuck.ShowFrame.Height LRedDuck.Load("../../../Images/L-Red-Duck-1.png", 1, 10) LRedDuck.Load("../../../Images/L-Red-Duck-2.png", 2, 10) LRedDuck.Load("../../../Images/L-Red-Duck-3.png", 3, 10) LRedDuck.UpdateFrame() LRedDuckRect.X = xna.Game1.Window.ClientBounds.Width LRedDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 LRedDuckRect.Width = LRedDuck.ShowFrame.Width LRedDuckRect.Height = LRedDuck.ShowFrame.Height End Sub End ModuleEnd Namespace
    [/spoiler]
    AnimatedTexture module:
    [spoiler]Imports Microsoft.Xna.FrameworkImports Microsoft.Xna.Framework.GraphicsNamespace xna Public Class AnimatedTexture Public ShowFrame As Texture2D Private Frame1 As Texture2D Private Frame2 As Texture2D Private Frame3 As Texture2D Private Frame4 As Texture2D Private FrameNbr As Byte = 1 Private TimePerFrame As Integer Public Sub Load(ByVal fileName As String, ByVal frame As Byte, ByVal tpf As Single) Select Case frame Case 1 Frame1 = Texture2D.FromFile(Device, fileName) Case 2 Frame2 = Texture2D.FromFile(Device, fileName) Frame4 = Texture2D.FromFile(Device, fileName) Case 3 Frame3 = Texture2D.FromFile(Device, fileName) End Select TimePerFrame = tpf End Sub Public Sub UpdateFrame() If RBlueDuckRect.X Mod TimePerFrame = 0 Then If FrameNbr < 4 Then FrameNbr += 1 Else FrameNbr = 1 End If End If Select Case FrameNbr Case 1 ShowFrame = Frame1 Case 2 ShowFrame = Frame2 Case 3 ShowFrame = Frame3 Case 4 ShowFrame = Frame4 End Select End Sub End ClassEnd Namespace
    [/spoiler]
    HitDetection module:
    [spoiler]Imports Microsoft.Xna.FrameworkImports Microsoft.Xna.Framework.InputNamespace xna Module HitDetection Public Sub Detect(ByVal x As Integer, ByVal y As Integer) If RBlueDuckRect.Contains(x, y) Then Score += 10 RBlueDuckRect.X = -80 RBlueDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If If LBlueDuckRect.Contains(x, y) Then Score += 10 LBlueDuckRect.X = xna.Game1.Window.ClientBounds.Width LBlueDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If If RGreenDuckRect.Contains(x, y) Then Score += 20 RGreenDuckRect.X = xna.Game1.Window.ClientBounds.Width RGreenDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If If LGreenDuckRect.Contains(x, y) Then Score += 20 LGreenDuckRect.X = xna.Game1.Window.ClientBounds.Width LGreenDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If If LRedDuckRect.Contains(x, y) Then Score += 30 LRedDuckRect.X = xna.Game1.Window.ClientBounds.Width LRedDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If If RRedDuckRect.Contains(x, y) Then Score += 30 RRedDuckRect.X = xna.Game1.Window.ClientBounds.Width RRedDuckRect.Y = Rnd() * xna.Game1.Window.ClientBounds.Height / 2 End If If DogRect1.Contains(x, y) OrElse DogRect2.Contains(x, y) Then Score += 50 DogJump = False DefineTextures.x = 200 DogRect1.X = -100 : DogRect1.Y = 0 DogRect2.X = -100 : DogRect2.Y = 0 End If End Sub End ModuleEnd Namespace
    [/spoiler]

    Year 2009 (I think): Battle Tanks v0



    30uzb14.jpg

    Wrote with C# (.NET 3.5) and XNA 3.1. This was my first try of making "player" move the objects using keyboard.
    This was also the project I learned why many of the modern games (League of Legends, for example) avoid using slick rotations of models unless needed.

    Project isn't particulary impressive. Just a box with a barrel moving around and rotating. With one interesting bug I didn't fix: pressing Space during rotation makes bullet stuck at that angle.

    Code is actually quite simple:
    [spoiler]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;namespace BattleTanks{ /// /// This is the main type for your game /// public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //Initialization private Texture2D Player1Tank; private Texture2D Bullet; private Vector2 TankOrig; private Vector2 TankPos; private Vector2 BulletPos; private Vector2 BulletOrig; private Single tankrotation; private Single bulletrotation; private Single scale; private Boolean BulletFired = false; private Boolean BulletFlying = false; private int BulletPower = 1; public Game1() { graphics = new GraphicsDeviceManager(this); graphics.GraphicsProfile = GraphicsProfile.Reach; Content.RootDirectory = "Content"; } /// /// 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. /// protected override void Initialize() { // TODO: Add your initialization logic here graphics.PreferredBackBufferHeight = 600; graphics.PreferredBackBufferWidth = 600; graphics.ApplyChanges(); base.Initialize(); } /// /// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here Player1Tank = Content.Load("Textures/Player1-tank"); Bullet = Content.Load("Textures/Bullet"); scale = 0.125f; TankPos.X = 25; TankPos.Y = 25; TankOrig.X = Player1Tank.Bounds.Center.X; TankOrig.Y = Player1Tank.Bounds.Center.Y; BulletOrig.X = Bullet.Bounds.Center.X; BulletOrig.Y = Bullet.Bounds.Center.Y; tankrotation = (float) Math.PI * 0.5f; } /// /// UnloadContent will be called once per game and is the place to unload /// all content. /// protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { // Allows the game to exit if (Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); // TODO: Add your update logic here #region "Player 1 movement" if (Keyboard.GetState().IsKeyDown(Keys.Right)) { if (Math.Round(tankrotation, 4) != Math.Round((float)Math.PI * 0.5f, 4)) { if ((tankrotation > (float)Math.PI * 1.5f) | (tankrotation < (float)Math.PI * 0.5f)) { tankrotation += (float)Math.PI * 0.0625f; if (tankrotation > (float)Math.PI * 2) tankrotation -= (float)Math.PI * 2; } else tankrotation -= (float)Math.PI * 0.0625f; } else if (TankPos.X < graphics.PreferredBackBufferWidth - Player1Tank.Bounds.Center.X * scale)TankPos.X += 2; } if (Keyboard.GetState().IsKeyDown(Keys.Left)) { if (Math.Round(tankrotation, 4) != Math.Round((float)Math.PI * 1.5f, 4)) { if ((tankrotation < (float)Math.PI * 0.5f) | (tankrotation > (float)Math.PI * 1.5f)) { tankrotation -= (float)Math.PI * 0.0625f; if (tankrotation < (float)Math.PI * 0) tankrotation += (float)Math.PI * 2; } else tankrotation += (float)Math.PI * 0.0625f; } else if (TankPos.X > Player1Tank.Bounds.Center.X * scale) TankPos.X -= 2; } if (Keyboard.GetState().IsKeyDown(Keys.Up)) { if (Math.Round(tankrotation, 4) != Math.Round((float)Math.PI * 0.0f, 4)) { if (tankrotation > (float)Math.PI) { tankrotation += (float)Math.PI * 0.0625f; if (tankrotation > (float)Math.PI * 2) tankrotation -= (float)Math.PI * 2; } else tankrotation -= (float)Math.PI * 0.0625f; } else if (TankPos.Y > Player1Tank.Bounds.Center.Y * scale) TankPos.Y -= 2; } if (Keyboard.GetState().IsKeyDown(Keys.Down)) { if (Math.Round(tankrotation, 4) != Math.Round((float)Math.PI * 1.0f, 4)) if (tankrotation > (float)Math.PI * 1.0f) { tankrotation -= (float)Math.PI * 0.0625f; if (tankrotation > (float)Math.PI * 2) tankrotation -= (float)Math.PI * 2; } else tankrotation += (float)Math.PI * 0.0625f; else if (TankPos.Y < graphics.PreferredBackBufferHeight - Player1Tank.Bounds.Center.Y * scale) TankPos.Y += 2; } #endregion #region "Bullet firing" if ((Keyboard.GetState().IsKeyDown(Keys.Space)) && (BulletFlying == false)) { BulletFired = true; BulletFlying = true; BulletPos.X = TankPos.X; BulletPos.Y = TankPos.Y; bulletrotation = tankrotation - (float)Math.PI * 0.5f; if (bulletrotation < -1) bulletrotation += (float)Math.PI * 2; } if (BulletFired == true) { if (Math.Round(bulletrotation, 4) == 0) BulletPos.X += 5 * BulletPower; if (Math.Round(bulletrotation, 4) == Math.Round((float)Math.PI * 0.5f, 4)) BulletPos.Y += 5 * BulletPower; if (Math.Round(bulletrotation, 4) == Math.Round((float)Math.PI, 4)) BulletPos.X -= 5 * BulletPower; if (Math.Round(bulletrotation, 4) == Math.Round((float)Math.PI * 1.5f, 4)) BulletPos.Y -= 5 * BulletPower; if ((BulletPos.X < 0) | (BulletPos.Y < 0) | (BulletPos.X > graphics.PreferredBackBufferWidth) | (BulletPos.Y > graphics.PreferredBackBufferHeight)) { BulletFired = false; BulletFlying = false; } } #endregion base.Update(gameTime); } /// /// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); // TODO: Add your drawing code here spriteBatch.Begin(); if (BulletFired == true) spriteBatch.Draw(Bullet, BulletPos, null, Color.White, bulletrotation, BulletOrig, scale, SpriteEffects.None, 0); spriteBatch.Draw(Player1Tank, TankPos, null, Color.Yellow, tankrotation, TankOrig, scale, SpriteEffects.None, 0); spriteBatch.End(); base.Draw(gameTime); } }}
    [/spoiler]

    I could say that the project is abandoned, but I won't; I used this project as a base for the one later.

    Year 2010: Nine Men's Morris



    2irr76o.jpg

    Back to the mouse based games. This one is once again written with C# (.NET 3.5) with XNA 3.1. I believe you can recognize the game, but in case you don't know, I'll explain:

    Two players, each has 9 pieces. Game is split into 2 phases.
    In phase 1, players alternately place their tokens on the board, trying to fill a row or column - make a "mill". After all 18 tokens are placed, each player can take away one of the opponents tokens from the board for each mill that player made. Tokens from mills cannot be taken out.
    In phase 2, each turn players can move one token to the adjacent cell. Goal is to take out all opponent tokens by making mills. Once player remains with only 3 tokens, he isn't restricted to the adjacent cells of each token and can "jump" over the whole board.

    Sadly, I never wrote a game beyond Phase 1. I might finish it one day... *looks at code* Screw it, if I ever decide to finish it I'll rather remake it from scratch:
    [spoiler]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;namespace Mlin{ /// /// This is the main type for your game /// public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; // Graphics specific variables public Texture2D Board; public Texture2D Figure; public Texture2D Background; public Rectangle BoardRect; public Double GraphicScale; public Vector2 FigurePosition; public Vector2 FigureOffset; public string Gamestate; public Color Player1Color; public Color Player2Color; // Game logic variables public Char[,] FigurePlacement = new char[7,7]; public byte PlayerTurn; public bool PositionPhase = true; public byte FigureCount1 = 0; public byte FigureCount2 = 0; public bool Selected = false; public byte SelectedPos; public byte x, y; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } /// /// 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. /// protected override void Initialize() { // TODO: Add your initialization logic here graphics.PreferredBackBufferHeight = 800; graphics.PreferredBackBufferWidth = 800; graphics.ApplyChanges(); IsMouseVisible = true; PlayerTurn = 1; Player1Color = Color.Red; Player2Color = Color.Blue; base.Initialize(); } /// /// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here Board = Content.Load("Images/Board"); Figure = Content.Load("Images/Figure"); // Setup BoardRect.Width = graphics.PreferredBackBufferWidth; BoardRect.Height = graphics.PreferredBackBufferHeight; BoardRect.X = 0; BoardRect.Y = 0; GraphicScale = (double) graphics.PreferredBackBufferHeight / Board.Height; FigureOffset.X = Figure.Bounds.Center.X; FigureOffset.Y = Figure.Bounds.Center.Y; } /// /// UnloadContent will be called once per game and is the place to unload /// all content. /// protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { // Allows the game to exit if (Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); // TODO: Add your update logic here if (Mouse.GetState().LeftButton == ButtonState.Pressed && PositionPhase == true) { #region Positions // First line if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[0, 0] == '\0') { FigurePlacement[0, 0] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[0, 0] == '\0') { FigurePlacement[0, 0] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[3, 0] == '\0') { FigurePlacement[3, 0] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[3, 0] == '\0') { FigurePlacement[3, 0] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[6, 0] == '\0') { FigurePlacement[6, 0] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[6, 0] == '\0') { FigurePlacement[6, 0] = '2'; ++FigureCount2; PlayerTurn = 1; } // Second line else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[1, 1] == '\0') { FigurePlacement[1, 1] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[1, 1] == '\0') { FigurePlacement[1, 1] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[3, 1] == '\0') { FigurePlacement[3, 1] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[3, 1] == '\0') { FigurePlacement[3, 1] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[5, 1] == '\0') { FigurePlacement[5, 1] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[5, 1] == '\0') { FigurePlacement[5, 1] = '2'; ++FigureCount2; PlayerTurn = 1; } // Third line else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[2, 2] == '\0') { FigurePlacement[2, 2] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[2, 2] == '\0') { FigurePlacement[2, 2] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[3, 2] == '\0') { FigurePlacement[3, 2] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[3, 2] == '\0') { FigurePlacement[3, 2] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[4, 2] == '\0') { FigurePlacement[4, 2] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[4, 2] == '\0') { FigurePlacement[4, 2] = '2'; ++FigureCount2; PlayerTurn = 1; } // 4th line else if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[0, 3] == '\0') { FigurePlacement[0, 3] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[0, 3] == '\0') { FigurePlacement[0, 3] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[1, 3] == '\0') { FigurePlacement[1, 3] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[1, 3] == '\0') { FigurePlacement[1, 3] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[2, 3] == '\0') { FigurePlacement[2, 3] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[2, 3] == '\0') { FigurePlacement[2, 3] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[4, 3] == '\0') { FigurePlacement[4, 3] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[4, 3] == '\0') { FigurePlacement[4, 3] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[5, 3] == '\0') { FigurePlacement[5, 3] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[5, 3] == '\0') { FigurePlacement[5, 3] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[6, 3] == '\0') { FigurePlacement[6, 3] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[6, 3] == '\0') { FigurePlacement[6, 3] = '2'; ++FigureCount2; PlayerTurn = 1; } // 5th line else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[2, 4] == '\0') { FigurePlacement[2, 4] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[2, 4] == '\0') { FigurePlacement[2, 4] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[3, 4] == '\0') { FigurePlacement[3, 4] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[3, 4] == '\0') { FigurePlacement[3, 4] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[4, 4] == '\0') { FigurePlacement[4, 4] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[4, 4] == '\0') { FigurePlacement[4, 4] = '2'; ++FigureCount2; PlayerTurn = 1; } // 6th line else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[1, 5] == '\0') { FigurePlacement[1, 5] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[1, 5] == '\0') { FigurePlacement[1, 5] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[3, 5] == '\0') { FigurePlacement[3, 5] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[3, 5] == '\0') { FigurePlacement[3, 5] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[5, 5] == '\0') { FigurePlacement[5, 5] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[5, 5] == '\0') { FigurePlacement[5, 5] = '2'; ++FigureCount2; PlayerTurn = 1; } // Last line else if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[0, 6] == '\0') { FigurePlacement[0, 6] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[0, 6] == '\0') { FigurePlacement[0, 6] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[3, 6] == '\0') { FigurePlacement[3, 6] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[3, 6] == '\0') { FigurePlacement[3, 6] = '2'; ++FigureCount2; PlayerTurn = 1; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale)) && PlayerTurn == 1 && FigurePlacement[6, 6] == '\0') { FigurePlacement[6, 6] = '1'; ++FigureCount1; PlayerTurn = 2; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale)) && PlayerTurn == 2 && FigurePlacement[6, 6] == '\0') { FigurePlacement[6, 6] = '2'; ++FigureCount2; PlayerTurn = 1; } #endregion if (FigureCount1 == 9 && FigureCount2 == 9) PositionPhase = false; } if (Mouse.GetState().LeftButton == ButtonState.Pressed && PositionPhase == false) { #region Selection decider if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale))) {SelectedPos = 1;} else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale))) {SelectedPos = 2;} else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(175 * GraphicScale)) && Mouse.GetState().Y < ((int)(325 * GraphicScale))) {SelectedPos = 3;} else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale))) {SelectedPos = 4;} else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale))) {SelectedPos = 5;} else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(375 * GraphicScale)) && Mouse.GetState().Y < ((int)(525 * GraphicScale))) {SelectedPos = 6;} else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale))) {SelectedPos = 7;} else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale))) {SelectedPos = 8;} else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(575 * GraphicScale)) && Mouse.GetState().Y < ((int)(725 * GraphicScale))) {SelectedPos = 9;} else if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale))) { SelectedPos = 10; } else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale))) { SelectedPos = 11; } else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale))) { SelectedPos = 12; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale))) { SelectedPos = 13; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale))) { SelectedPos = 14; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(925 * GraphicScale)) && Mouse.GetState().Y < ((int)(1075 * GraphicScale))) { SelectedPos = 15; } else if (Mouse.GetState().X > ((int)(575 * GraphicScale)) && Mouse.GetState().X < ((int)(725 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale))) { SelectedPos = 16; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale))) { SelectedPos = 17; } else if (Mouse.GetState().X > ((int)(1275 * GraphicScale)) && Mouse.GetState().X < ((int)(1425 * GraphicScale)) && Mouse.GetState().Y > ((int)(1275 * GraphicScale)) && Mouse.GetState().Y < ((int)(1425 * GraphicScale))) { SelectedPos = 18; } else if (Mouse.GetState().X > ((int)(375 * GraphicScale)) && Mouse.GetState().X < ((int)(525 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale))) { SelectedPos = 19; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale))) { SelectedPos = 20; } else if (Mouse.GetState().X > ((int)(1475 * GraphicScale)) && Mouse.GetState().X < ((int)(1625 * GraphicScale)) && Mouse.GetState().Y > ((int)(1475 * GraphicScale)) && Mouse.GetState().Y < ((int)(1625 * GraphicScale))) { SelectedPos = 21; } else if (Mouse.GetState().X > ((int)(175 * GraphicScale)) && Mouse.GetState().X < ((int)(325 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale))) { SelectedPos = 22; } else if (Mouse.GetState().X > ((int)(925 * GraphicScale)) && Mouse.GetState().X < ((int)(1075 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale))) { SelectedPos = 23; } else if (Mouse.GetState().X > ((int)(1675 * GraphicScale)) && Mouse.GetState().X < ((int)(1825 * GraphicScale)) && Mouse.GetState().Y > ((int)(1675 * GraphicScale)) && Mouse.GetState().Y < ((int)(1825 * GraphicScale))) { SelectedPos = 24; } #endregion } base.Update(gameTime); } /// /// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.Draw(Board, BoardRect, Color.Yellow); #region Figure drawing // First line if (FigurePlacement[0, 0] == '1') { FigurePosition.X = (int)(250 * GraphicScale); FigurePosition.Y = (int)(250 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color , 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[0, 0] == '2') { FigurePosition.X = (int)(250 * GraphicScale); FigurePosition.Y = (int)(250 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 0] == '1') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(250 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 0] == '2') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(250 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[6, 0] == '1') { FigurePosition.X = (int)(1750 * GraphicScale); FigurePosition.Y = (int)(250 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[6, 0] == '2') { FigurePosition.X = (int)(1750 * GraphicScale); FigurePosition.Y = (int)(250 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } // Second line if (FigurePlacement[1, 1] == '1') { FigurePosition.X = (int)(450 * GraphicScale); FigurePosition.Y = (int)(450 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[1, 1] == '2') { FigurePosition.X = (int)(450 * GraphicScale); FigurePosition.Y = (int)(450 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 1] == '1') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(450 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 1] == '2') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(450 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[5, 1] == '1') { FigurePosition.X = (int)(1550 * GraphicScale); FigurePosition.Y = (int)(450 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[5, 1] == '2') { FigurePosition.X = (int)(1550 * GraphicScale); FigurePosition.Y = (int)(450 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } // 3rd line if (FigurePlacement[2, 2] == '1') { FigurePosition.X = (int)(650 * GraphicScale); FigurePosition.Y = (int)(650 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[2, 2] == '2') { FigurePosition.X = (int)(650 * GraphicScale); FigurePosition.Y = (int)(650 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 2] == '1') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(650 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 2] == '2') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(650 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[4, 2] == '1') { FigurePosition.X = (int)(1350 * GraphicScale); FigurePosition.Y = (int)(650 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[4, 2] == '2') { FigurePosition.X = (int)(1350 * GraphicScale); FigurePosition.Y = (int)(650 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } // 4th line if (FigurePlacement[0, 3] == '1') { FigurePosition.X = (int)(250 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[0, 3] == '2') { FigurePosition.X = (int)(250 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[1, 3] == '1') { FigurePosition.X = (int)(450 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[1, 3] == '2') { FigurePosition.X = (int)(450 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[2, 3] == '1') { FigurePosition.X = (int)(650 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[2, 3] == '2') { FigurePosition.X = (int)(650 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[4, 3] == '1') { FigurePosition.X = (int)(1350 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[4, 3] == '2') { FigurePosition.X = (int)(1350 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[5, 3] == '1') { FigurePosition.X = (int)(1550 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[5, 3] == '2') { FigurePosition.X = (int)(1550 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[6, 3] == '1') { FigurePosition.X = (int)(1750 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[6, 3] == '2') { FigurePosition.X = (int)(1750 * GraphicScale); FigurePosition.Y = (int)(1000 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } // 5th line if (FigurePlacement[2, 4] == '1') { FigurePosition.X = (int)(650 * GraphicScale); FigurePosition.Y = (int)(1350 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[2, 4] == '2') { FigurePosition.X = (int)(650 * GraphicScale); FigurePosition.Y = (int)(1350 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 4] == '1') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(1350 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 4] == '2') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(1350 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[4, 4] == '1') { FigurePosition.X = (int)(1350 * GraphicScale); FigurePosition.Y = (int)(1350 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[4, 4] == '2') { FigurePosition.X = (int)(1350 * GraphicScale); FigurePosition.Y = (int)(1350 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } // 6th line if (FigurePlacement[1, 5] == '1') { FigurePosition.X = (int)(450 * GraphicScale); FigurePosition.Y = (int)(1550 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[1, 5] == '2') { FigurePosition.X = (int)(450 * GraphicScale); FigurePosition.Y = (int)(1550 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 5] == '1') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(1550 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 5] == '2') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(1550 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[5, 5] == '1') { FigurePosition.X = (int)(1550 * GraphicScale); FigurePosition.Y = (int)(1550 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[5, 5] == '2') { FigurePosition.X = (int)(1550 * GraphicScale); FigurePosition.Y = (int)(1550 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } // Last line if (FigurePlacement[0, 6] == '1') { FigurePosition.X = (int)(250 * GraphicScale); FigurePosition.Y = (int)(1750 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[0, 6] == '2') { FigurePosition.X = (int)(250 * GraphicScale); FigurePosition.Y = (int)(1750 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 6] == '1') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(1750 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[3, 6] == '2') { FigurePosition.X = (int)(1000 * GraphicScale); FigurePosition.Y = (int)(1750 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[6, 6] == '1') { FigurePosition.X = (int)(1750 * GraphicScale); FigurePosition.Y = (int)(1750 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player1Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } if (FigurePlacement[6, 6] == '2') { FigurePosition.X = (int)(1750 * GraphicScale); FigurePosition.Y = (int)(1750 * GraphicScale); spriteBatch.Draw(Figure, FigurePosition, null, Player2Color, 0.0f, FigureOffset, (float)GraphicScale, SpriteEffects.None, 0); } #endregion spriteBatch.End(); base.Draw(gameTime); } }}
    [/spoiler]

    I wasn't that young anymore. I just finished high school and entered faculty (college, if you like). I learned QBasic, (Turbo) Pascal, Visual Basic and C# until that point and I was comfortable with Trigger Editor in WarCraft 3 World Editor. So, I have full right to ask this: "WHAT THE HELL WAS I THINKING!!!"

    Year 2011: Arkanoid

    (more like "Half Pong")

    6gvreo.jpg

    This one is also uncomplete. Written with C# (.NET 4.0) and XNA 4.0, I managed to get the basics (paddle moving and ball bouncing) before giving up for some reason. I think the reason was making the collision detection work the way I imagined it. While many people finished it (most recent example being superman3275), I decided to skip it... for now.

    Code is actually quite clean, however I think I made it more complicated than I should:
    [spoiler]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;namespace Arkanoid{ /// /// This is the main type for your game /// public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //variable private Texture2D Background; private Rectangle BackgroundRect; private Texture2D Bat; private Rectangle BatBorder; private Texture2D Ball; private Vector2 BallCenter; private Vector2 BallOffset; private bool BallLaunch; private double BallAngle; private int BallSpeed; private int BallRadius; private Texture2D Brick; private Rectangle[] BrickBorder = new Rectangle[30]; private double x; private Point Helper; public Game1() { graphics = new GraphicsDeviceManager(this); graphics.GraphicsProfile = GraphicsProfile.Reach; Content.RootDirectory = "Content"; } /// /// 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. /// protected override void Initialize() { // TODO: Add your initialization logic here graphics.PreferredBackBufferWidth = 500; graphics.PreferredBackBufferHeight = 600; graphics.ApplyChanges(); base.Initialize(); } /// /// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here Bat = Content.Load("Textures/Bat"); BatBorder.Width = Bat.Width / 4; BatBorder.Height = 15; BatBorder.X = graphics.PreferredBackBufferWidth / 2 - Bat.Width / 8; BatBorder.Y = 550; Ball = Content.Load("Textures/Ball"); BallRadius = 10; BallOffset.X = Ball.Bounds.Center.X; BallOffset.Y = Ball.Bounds.Center.Y; BallCenter.X = graphics.PreferredBackBufferWidth / 2; BallCenter.Y = BatBorder.Y - BallRadius - 1; BallAngle = Math.PI * 5 / 4; BallSpeed = 2; BallLaunch = false; } /// /// UnloadContent will be called once per game and is the place to unload /// all content. /// protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { // Allows the game to exit if (Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); // TODO: Add your update logic here #region Bat movement if ((Keyboard.GetState().IsKeyDown(Keys.Left)) && (BatBorder.Left >= 0)) { BatBorder.X -= 5; if (BallLaunch == false) { BallAngle = Math.PI * 5 / 4; BallCenter.X -= 5; } } if ((Keyboard.GetState().IsKeyDown(Keys.Right)) && (BatBorder.Right <= graphics.PreferredBackBufferWidth)) { BatBorder.X += 5; if (BallLaunch == false) { BallAngle = Math.PI * 7 / 4; BallCenter.X += 5; } } if (Keyboard.GetState().IsKeyDown(Keys.Space)) BallLaunch = true; #endregion #region Collision detection //Paziti na koordinatni sustav: ide CW, ne CCW kao inace //Znaci, 45? normalno je PI * 7 / 4 //Kolizija s rubom igraceg prostora if ((BallCenter.X <= BallRadius) || (BallCenter.X >= graphics.PreferredBackBufferWidth - BallRadius)) BallAngle = 3 * Math.PI - BallAngle; if (BallCenter.Y <= 10) BallAngle = 2 * Math.PI - BallAngle; #endregion //Pomak lopte if (BallLaunch) { for (x = 0; x <= BallSpeed; x++) { BallCenter.X += 1 / (float)Math.Cos(BallAngle); BallCenter.Y += 1 / (float)Math.Sin(BallAngle); //Kolizija if (((BallCenter.Y > (BatBorder.Top - BallRadius)) && (BallCenter.Y < (BatBorder.Bottom + BallRadius))) && ((BallCenter.X > (BatBorder.Left - BallRadius)) && (BallCenter.X < (BatBorder.Right + BallRadius)))) { BallAngle = 2 * Math.PI - BallAngle; break; } } } base.Update(gameTime); } /// /// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.DarkBlue); // TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.Draw(Bat, BatBorder, Color.LightGreen); spriteBatch.Draw(Ball, BallCenter, null, Color.Yellow, 0.0f, BallOffset, 0.05f, SpriteEffects.None, 1.0f); spriteBatch.End(); base.Draw(gameTime); } }}
    [/spoiler]

    Year 2012: Battle Tanks



    [sharedmedia=gallery:images:2885]

    This one should actually be familiar if you remember anything from my older journal entries. I won't ramble too much about it; based on "Box with barrel", it's actually the 2nd sensible thing I made after a huge gap and a huge step up. I slowly started to do actual object-oriented programming. I've wrote more about it in my earlier entries.

    For anyone interested in code:

    Game1 class:
    [spoiler]using System;using System.Collections.Generic;using System.Linq;using System.IO;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 Microsoft.Xna.Framework.Net;namespace BattleTanks{ /// /// This is the main type for your game /// public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } #region Variables Texture2D PlayableArea; Rectangle RectPlayable; Tank[] Tanks; Tile[] Metal; Tile[] Grass; Tile[] Water; Tile Castle; Player Player1; Player Player2; byte NumOpponents = 30; SpriteFont Font1; Vector2 Score1Text; Vector2 Score2Text; Vector2 NumberOppText; Vector2 P1LivesText; Vector2 P2LivesText; Vector2 EndText; Rectangle[] SpawnPoints; TimeSpan Time; byte PointBonus = 100; bool GameOver = false; bool Victory = false; #endregion /// /// 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. /// protected override void Initialize() { // TODO: Add your initialization logic here graphics.PreferredBackBufferWidth = 800; graphics.PreferredBackBufferHeight = 600; graphics.ApplyChanges(); base.Initialize(); } /// /// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here // Load all textures PlayableArea = this.Content.Load("PlayableArea"); Font1 = this.Content.Load("Arial"); // Set up playable area RectPlayable.X = 0; RectPlayable.Y = 0; RectPlayable.Width = PlayableArea.Width; RectPlayable.Height = PlayableArea.Height; LevelLoad(); Player1 = new Player(); Player2 = new Player(); Player1.Initialize(Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Enter); Player2.Initialize(Keys.W, Keys.S, Keys.A, Keys.D, Keys.Space); Score1Text = new Vector2(610, 20); Score2Text = new Vector2(610, 70); NumberOppText = new Vector2(610, 150); P1LivesText = new Vector2(610, 300); P2LivesText = new Vector2(610, 330); EndText = new Vector2(RectPlayable.Center.X, RectPlayable.Center.Y); } /// /// UnloadContent will be called once per game and is the place to unload /// all content. /// protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { // Allows the game to exit if (Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); // TODO: Add your update logic here if ((Player1.PlayerLives == 0) & (Player2.PlayerLives == 0)) { GameOver = true; Victory = false; } if (NumOpponents == 0) { GameOver = true; Victory = true; } if (GameOver == false) { Time = gameTime.TotalGameTime; TankRespawn(); #region Player 1 movement // Self explanatory if (Keyboard.GetState().IsKeyDown(Keys.Left)) { Tanks[0].MoveTank(true, -1, false); if (Collision(Tanks[0].TankHitbox, false, true)) Tanks[0].MoveTank(true, 1, true); } else if (Keyboard.GetState().IsKeyDown(Keys.Right)) { Tanks[0].MoveTank(true, 1, false); if (Collision(Tanks[0].TankHitbox, false, true)) Tanks[0].MoveTank(true, -1, true); } else if (Keyboard.GetState().IsKeyDown(Keys.Up)) { Tanks[0].MoveTank(false, -1, false); if (Collision(Tanks[0].TankHitbox, false, true)) Tanks[0].MoveTank(false, 1, true); } else if (Keyboard.GetState().IsKeyDown(Keys.Down)) { Tanks[0].MoveTank(false, 1, false); if (Collision(Tanks[0].TankHitbox, false, true)) Tanks[0].MoveTank(false, -1, true); } if ((Keyboard.GetState().IsKeyDown(Keys.Enter)) && (Tanks[0].BulletLaunched == false)) { Tanks[0].LaunchBullet(); } if (Tanks[0].BulletLaunched) { if (Collision(Tanks[0].BulletHitBox, true, true) == true) Tanks[0].RemoveBullet(); else Tanks[0].MoveBullet(); } #endregion #region Player 2 movement // Self explanatory if (Keyboard.GetState().IsKeyDown(Keys.A)) { Tanks[1].MoveTank(true, -1, false); if (Collision(Tanks[1].TankHitbox, false, true)) Tanks[1].MoveTank(true, 1, true); } else if (Keyboard.GetState().IsKeyDown(Keys.D)) { Tanks[1].MoveTank(true, 1, false); if (Collision(Tanks[1].TankHitbox, false, true)) Tanks[1].MoveTank(true, -1, true); } else if (Keyboard.GetState().IsKeyDown(Keys.W)) { Tanks[1].MoveTank(false, -1, false); if (Collision(Tanks[1].TankHitbox, false, true)) Tanks[1].MoveTank(false, 1, true); } else if (Keyboard.GetState().IsKeyDown(Keys.S)) { Tanks[1].MoveTank(false, 1, false); if (Collision(Tanks[1].TankHitbox, false, true)) Tanks[1].MoveTank(false, -1, true); } if ((Keyboard.GetState().IsKeyDown(Keys.Space)) && (Tanks[1].BulletLaunched == false)) { Tanks[1].LaunchBullet(); } if (Tanks[1].BulletLaunched) { if (Collision(Tanks[1].BulletHitBox, true, true) == true) Tanks[1].RemoveBullet(); else Tanks[1].MoveBullet(); } #endregion #region AI movement bool axis; int dir; string srdir; for (int x = 2; x < Tanks.Length; x++) { if (Tanks[x].TankDestroyed == false) { // Determine direction for movement Tanks[x].RotateAI(out axis, out dir, out srdir); // Move AI tank Tanks[x].MoveTank(axis, dir, false); // If collided with something, reposition tank properly and determine new direction if (Collision(Tanks[x].TankHitbox, false, false)) { Tanks[x].MoveTank(axis, -1 * dir, true); Tanks[x].TurnAI(srdir); } // Fire bullet if (Tanks[x].BulletLaunched == false) Tanks[x].FireAI(); } if (Tanks[x].BulletLaunched) { if (Collision(Tanks[x].BulletHitBox, true, false) == true) Tanks[x].RemoveBullet(); else Tanks[x].MoveBullet(); } } #endregion if (Keyboard.GetState().IsKeyDown(Keys.F1)) Player1.PlayerLives = 99; if (Keyboard.GetState().IsKeyDown(Keys.F2)) Player2.PlayerLives = 99; } base.Update(gameTime); } /// /// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.LightGray); // TODO: Add your drawing code here spriteBatch.Begin(); // Draw area spriteBatch.Draw(PlayableArea, RectPlayable, Color.White); if (GameOver == false) { for (int x = 0; x < Water.Length; x++) Water[x].Draw(spriteBatch); // Draw tanks and respective bullets for (int x = 0; x < Tanks.Length; x++) Tanks[x].Draw(spriteBatch); // Draw tiles walls Castle.Draw(spriteBatch); for (int x = 0; x < Metal.Length; x++) Metal[x].Draw(spriteBatch); for (int x = 0; x < Grass.Length; x++) Grass[x].Draw(spriteBatch); } if (GameOver == true) { if (Victory == true) spriteBatch.DrawString(Font1, "VICTORY!", EndText, Color.Red); else spriteBatch.DrawString(Font1, "GAME OVER!", EndText, Color.Red); } // Draw text string output; output = "Player 1 score: \n" + Player1.Score.ToString(); spriteBatch.DrawString(Font1, output, Score1Text, Color.Black); output = "Player 2 score: \n" + Player2.Score.ToString(); spriteBatch.DrawString(Font1, output, Score2Text, Color.Black); output = "Remaining Enemies: " + NumOpponents.ToString(); spriteBatch.DrawString(Font1, output, NumberOppText, Color.Black); output = "Player 1 lives: " + Player1.PlayerLives.ToString(); spriteBatch.DrawString(Font1, output, P1LivesText, Color.Black); output = "Player 2 lives: " + Player2.PlayerLives.ToString(); spriteBatch.DrawString(Font1, output, P2LivesText, Color.Black); spriteBatch.End(); base.Draw(gameTime); } public bool Collision(Rectangle Object, bool Projectile, bool PlayerTank) { // Is inside playing field? if (RectPlayable.Contains(Object) == false) return true; // Interaction between bullets and tanks for (int x = 0; x < Tanks.Length; x++) { if (Projectile) { // Collision between 2 bullets if ((Object != Tanks[x].BulletHitBox) & (Object.Intersects(Tanks[x].BulletHitBox))) { Tanks[x].RemoveBullet(); return true; } // Bullet hits tank if ((Object != Tanks[x].BulletHitBox) & (PlayerTank != Tanks[x].PlayerBullet) & (Object.Intersects(Tanks[x].TankHitbox))) { if (x == 0) Player1.PlayerDied(); else if (x == 1) Player2.PlayerDied(); else if (Object == Tanks[0].BulletHitBox) { Player1.PlayerKilled(PointBonus); --NumOpponents; } else if (Object == Tanks[1].BulletHitBox) { Player2.PlayerKilled(PointBonus); --NumOpponents; } Tanks[x].RemoveTank(Time); return true; } if ((Object != Tanks[x].BulletHitBox) & (Object.Intersects(Castle.TileRectangle))) GameOver = true; } // Bullet and tank collides with any tank if ((Object != Tanks[x].TankHitbox) & (!Object.Contains(Tanks[x].BulletHitBox)) & (Object.Intersects(Tanks[x].TankHitbox))) return true; } // Bullet and tank collides with metal for (int x = 0; x < Metal.Length; x++) if (Object.Intersects(Metal[x].TileRectangle)) return true; // Tank collides with water for (int x = 0; x < Water.Length; x++) if ((Object.Intersects(Water[x].TileRectangle)) && (Projectile == false)) return true; return false; } void LevelLoad() { string path = @"..\..\..\level1.txt"; int metalnum, grassnum, waternum, spawnnum; string[] level = new string[15]; // Read level using (StreamReader sr = File.OpenText(path)) { for (int i = 0; i < level.Length; i++) { level = sr.ReadLine(); } } metalnum = 0; grassnum = 0; waternum = 0; spawnnum = 2; for (int x = 0; x < level.Length; x++) { for (int y = 0; y < level[x].Length; y++) { if (level[x][y] == 'M') metalnum++; if (level[x][y] == 'B') metalnum++; if (level[x][y] == 'G') grassnum++; if (level[x][y] == 'W') waternum++; if (level[x][y] == 'S') spawnnum++; } } Metal = new Tile[metalnum]; Grass = new Tile[grassnum]; Water = new Tile[waternum]; Tanks = new Tank[spawnnum]; SpawnPoints = new Rectangle[spawnnum]; metalnum = 0; grassnum = 0; waternum = 0; spawnnum = 2; #region Tile placement for (int x = 0; x < level.Length; x++) { for (int y = 0; y < level[x].Length; y++) { switch (level[x][y]) { case 'M': { Metal[metalnum] = new Tile(); Metal[metalnum].LoadContent(this.Content, "Metal"); Metal[metalnum].Initialize(y * 40, x * 40, 0.1f); metalnum++; break; } case 'B': { Metal[metalnum] = new Tile(); Metal[metalnum].LoadContent(this.Content, "Brick"); Metal[metalnum].Initialize(y * 40, x * 40, 0.2f); metalnum++; break; } case 'G': { Grass[grassnum] = new Tile(); Grass[grassnum].LoadContent(this.Content, "Grass"); Grass[grassnum].Initialize(y * 40, x * 40, 0.2f); grassnum++; break; } case 'W': { Water[waternum] = new Tile(); Water[waternum].LoadContent(this.Content, "Water"); Water[waternum].Initialize(y * 40, x * 40, 0.2f); waternum++; break; } case '1': { SpawnPoints[0].Width = 40; SpawnPoints[0].Height = 40; SpawnPoints[0].Y = x * SpawnPoints[0].Width; SpawnPoints[0].X = y * SpawnPoints[0].Height; break; } case '2': { SpawnPoints[1].Width = 40; SpawnPoints[1].Height = 40; SpawnPoints[1].Y = x * SpawnPoints[1].Width; SpawnPoints[1].X = y * SpawnPoints[1].Height; break; } case 'C': { Castle = new Tile(); Castle.LoadContent(this.Content, "Castle"); Castle.Initialize(y * 40, x * 40, 0.1f); break; } case 'S': { SpawnPoints[spawnnum].Width = 40; SpawnPoints[spawnnum].Height = 40; SpawnPoints[spawnnum].Y = x * SpawnPoints[spawnnum].Width; SpawnPoints[spawnnum].X = y * SpawnPoints[spawnnum].Height; spawnnum++; break; } } } } #endregion #region Spawn Tanks for (int x = 0; x < Tanks.Length; x++) { Tanks[x] = new Tank(); if (x == 0) Tanks[x].LoadContent(this.Content, "Player1", "Bullet"); else if (x == 1) Tanks[x].LoadContent(this.Content, "Player2", "Bullet"); else Tanks[x].LoadContent(this.Content, "AITank", "Bullet"); if (x == 0) Tanks[x].Initialize(SpawnPoints[x].X, SpawnPoints[x].Y, 0, 0.4f, 2, true); else if (x == 1) Tanks[x].Initialize(SpawnPoints[x].X, SpawnPoints[x].Y, 0, 0.4f, 2, true); else Tanks[x].Initialize(SpawnPoints[x].X, SpawnPoints[x].Y, (float)Math.PI, 0.4f, 1, false); } #endregion } void TankRespawn() { bool OnSpawn; for (int x = 0; x < Tanks.Length; x++) { OnSpawn = false; for (int y = 0; y < Tanks.Length; y++) if ((Tanks[x].TankDestroyed) & (SpawnPoints[x].Intersects(Tanks[y].TankHitbox))) OnSpawn = true; if ((Tanks[x].TankDestroyed) & (!OnSpawn) & (Time.TotalSeconds - Tanks[x].TimeDestroyed.TotalSeconds >= 5)) { Tanks[x] = new Tank(); if ((x == 0) & (Player1.PlayerLives > 0)) Tanks[x].LoadContent(this.Content, "Player1", "Bullet"); else if ((x == 1) & (Player2.PlayerLives > 0)) Tanks[x].LoadContent(this.Content, "Player2", "Bullet"); else if (NumOpponents > 1) Tanks[x].LoadContent(this.Content, "AITank", "Bullet"); if ((x == 0) & (Player1.PlayerLives > 0)) Tanks[x].Initialize(SpawnPoints[x].X, SpawnPoints[x].Y, 0, 0.4f, 2, true); else if ((x == 1) & (Player2.PlayerLives > 0)) Tanks[x].Initialize(SpawnPoints[x].X, SpawnPoints[x].Y, 0, 0.4f, 2, true); else if (NumOpponents > 1) Tanks[x].Initialize(SpawnPoints[x].X, SpawnPoints[x].Y, (float)Math.PI, 0.4f, 1, false); } } } }}
    [/spoiler]

    Player class:
    [spoiler]using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Input;namespace BattleTanks{ class Player { private Keys MoveUp; private Keys MoveDown; private Keys MoveLeft; private Keys MoveRight; private Keys Fire; public byte PlayerLives; public UInt32 Score; public float ScoreMultiplier; public void Initialize(Keys Up, Keys Down, Keys Left, Keys Right, Keys Shoot) { // This part is currently useless MoveUp = Up; MoveDown = Down; MoveLeft = Left; MoveRight = Right; Fire = Shoot; PlayerLives = 3; Score = 0; ScoreMultiplier = 1.0f; } public void PlayerDied() { PlayerLives--; ScoreMultiplier = 1.0f; } public void PlayerKilled(byte Points) { Score += (UInt32)(Points * ScoreMultiplier); ScoreMultiplier += 0.1f; } }}
    [/spoiler]

    Tank class:
    [spoiler]using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Content;using Microsoft.Xna.Framework.Graphics;namespace BattleTanks{ class Tank { // Textures private Texture2D TankTexture; private Texture2D BulletTexture; // Tank parameters public Rectangle TankHitbox; public bool TankDestroyed; public TimeSpan TimeDestroyed; private Vector2 TankPosition; private Vector2 TankOrigin; private float TankRotation; private float TankScale; private int TankSpeed; // Bullet parameters public Rectangle BulletHitBox; public bool BulletLaunched = false; public bool PlayerBullet; private Vector2 BulletPosition; private Vector2 BulletOrigin; private int BulletSpeed = 5; private float BulletScale; private float BulletRotation; // AI parameters private int chanceTop; private int chanceLeft; private int chanceBottom; private int chanceRight; private int chanceFire = 50; private Random rnd; // Load all required content public void LoadContent(ContentManager content, string TankAssetName, string BulletAssetName) { TankTexture = content.Load(TankAssetName); BulletTexture = content.Load(BulletAssetName); } // Draw sprites public void Draw(SpriteBatch spriteBatch) { if (BulletLaunched) spriteBatch.Draw(BulletTexture, BulletPosition, null, Color.White, BulletRotation, BulletOrigin, BulletScale, SpriteEffects.None, 0); if (TankDestroyed == false) spriteBatch.Draw(TankTexture, TankPosition, null, Color.White, TankRotation, TankOrigin, TankScale, SpriteEffects.None, 0); } public void Initialize(int X, int Y, float rotation, float scaling, int speed, bool playerbullet) { // Tank properties TankRotation = rotation; TankScale = scaling; TankHitbox.X = X + 4; TankHitbox.Y = Y + 4; TankHitbox.Width = (int)(TankTexture.Width * scaling) - 8; TankHitbox.Height = (int)(TankTexture.Height * scaling) - 8; TankPosition.X = TankHitbox.Center.X; TankPosition.Y = TankHitbox.Center.Y; TankOrigin.X = TankTexture.Bounds.Center.X; TankOrigin.Y = TankTexture.Bounds.Center.Y; TankSpeed = speed; TankDestroyed = false; // Randomize rnd = new Random(); chanceBottom = 25; chanceLeft = 25; chanceRight = 25; chanceTop = 25; PlayerBullet = playerbullet; } public void MoveTank(bool HorAxis, int direction, bool cancel) { if (HorAxis) { // Moves tank on horizontal axis TankHitbox.X += TankSpeed * direction; TankPosition.X += TankSpeed * direction; if (!cancel) { if (direction < 0) TankRotation = 3 * (float)Math.PI / 2; if (direction > 0) TankRotation = (float)Math.PI / 2; } } else { // Moves tank on vertical axis TankHitbox.Y += TankSpeed * direction; TankPosition.Y += TankSpeed * direction; if (!cancel) { if (direction < 0) TankRotation = 0; if (direction > 0) TankRotation = (float)Math.PI; } } } public void RemoveTank(TimeSpan time) { TimeDestroyed = time; TankDestroyed = true; TankHitbox.X = 0; TankHitbox.Y = 0; TankHitbox.Width = 0; TankHitbox.Height = 0; } public void MoveBullet() { // Self explanatory if (BulletRotation == 0) { BulletHitBox.Y -= BulletSpeed; BulletPosition.Y -= BulletSpeed; } if (BulletRotation == (float)Math.PI / 2) { BulletHitBox.X += BulletSpeed; BulletPosition.X += BulletSpeed; } if (BulletRotation == (float)Math.PI) { BulletHitBox.Y += BulletSpeed; BulletPosition.Y += BulletSpeed; } if (BulletRotation == 3 * (float)Math.PI / 2) { BulletHitBox.X -= BulletSpeed; BulletPosition.X -= BulletSpeed; } } public void LaunchBullet() { // Creates and launches new bullet BulletScale = TankScale / 4; if ((TankRotation == 0) || (TankRotation == (float)Math.PI)) { BulletHitBox.Width = (int)(BulletTexture.Bounds.Width * BulletScale); BulletHitBox.Height = (int)(BulletTexture.Bounds.Height * BulletScale); } else { BulletHitBox.Width = (int)(BulletTexture.Bounds.Height * BulletScale); BulletHitBox.Height = (int)(BulletTexture.Bounds.Width * BulletScale); } BulletOrigin.X = BulletTexture.Bounds.Center.X; BulletOrigin.Y = BulletTexture.Bounds.Center.Y; BulletHitBox.X = TankHitbox.Center.X - BulletHitBox.Width / 2; BulletHitBox.Y = TankHitbox.Center.Y - BulletHitBox.Height / 2; BulletPosition.X = BulletHitBox.Center.X; BulletPosition.Y = BulletHitBox.Center.Y; BulletRotation = TankRotation; BulletLaunched = true; } public string BulletDirection() { // Returns bullet's current direction if (BulletRotation == 0) return "Up"; if (BulletRotation == (float)Math.PI / 2) return "Right"; if (BulletRotation == (float)Math.PI) return "Down"; if (BulletRotation == 3 * (float)Math.PI / 2) return "Left"; return null; } public void RemoveBullet() { BulletLaunched = false; BulletHitBox.X = 0; BulletHitBox.Y = 0; BulletHitBox.Width = 0; BulletHitBox.Height = 0; } public void RotateAI(out bool HorAxis, out int direction, out string text) { int dir; int left = chanceTop + chanceLeft; int bot = left + chanceBottom; HorAxis = false; direction = 0; text = null; dir = rnd.Next(0, 100); // Determine direction if ((0 < dir) & (dir <= chanceTop)) TankRotation = 0; if ((chanceTop < dir) & (dir <= left)) TankRotation = 3 * (float)Math.PI / 2; if ((left < dir) & (dir <= bot)) TankRotation = (float)Math.PI; if ((bot < dir) & (dir <= 100)) TankRotation = (float)Math.PI / 2; // Adjust direction priorities - currently moving direction has top priority if (TankRotation == 0) { HorAxis = false; direction = -1; text = "Up"; chanceTop = 97; chanceLeft = 1; chanceRight = 1; chanceBottom = 1; } if (TankRotation == (float)Math.PI / 2) { HorAxis = true; direction = 1; text = "Right"; chanceTop = 1; chanceLeft = 1; chanceRight = 97; chanceBottom = 1; } if (TankRotation == (float)Math.PI) { HorAxis = false; direction = 1; text = "Down"; chanceTop = 1; chanceLeft = 1; chanceRight = 1; chanceBottom = 97; } if (TankRotation == 3 * (float)Math.PI / 2) { HorAxis = true; direction = -1; text = "Left"; chanceTop = 1; chanceLeft = 97; chanceRight = 1; chanceBottom = 1; } } public void FireAI() { // AI fires bullet if (rnd.Next(0, 100) >= chanceFire) { LaunchBullet(); } } public void TurnAI(string direction) { // Changes priorities for direction switch (direction) { case "Left": { chanceTop = 32; chanceLeft = 4; chanceRight = 32; chanceBottom = 32; break; } case "Right": { chanceTop = 32; chanceLeft = 32; chanceRight = 4; chanceBottom = 32; break; } case "Up": { chanceTop = 4; chanceLeft = 32; chanceRight = 32; chanceBottom = 32; break; } case "Down": { chanceTop = 32; chanceLeft = 32; chanceRight = 32; chanceBottom = 4; break; } } } }}
    [/spoiler]

    Year 2013: Battle City



    6ymgbl.jpg

    Upgrade of Battle Tanks. I'm still working on it - on both code and art. I won't ramble much about it here, and just add: powerups are added.

    Now, take the picture above and compare it with this:
    30uzb14.jpg

    However, the biggest step is not the art - it's the code. Up to the Battle Tanks, I used strictly arrays and simple structures/classes and used only main class (the one automatically created with project), with a lot of copy/pasting, bad code etc. Now, with Battle City, I finally stepped - at least with one leg - into "true" object oriented programming. Skills I learned here helped me to deal with other tasks faster and easier. For example, laboratory tasks (like writing Finite State Machine) took no longer than 20 minutes of writing and debugging.

    Battle City is nowhere near completition, but here are code metrics for current state:

    • Maintanability Index: 78
    • Cyclomatic Complexity: 873
    • Depth of Inheritance: 3
    • Class Coupling: 85
    • Lines of Code: 2025

      If you wish to try it, head to my last entry and grab the attachement. That version doesn't have powerups but is still playable; I'd appreciate a feedback. I hope I'll be able to provide better version soon enough.

      I hope this little trip to the past was at least a little interesting. If nothing, I'll have this journal preserving my memories and as a reminder how I was before as a programmer.

      Thanks for reading and, I hope, see you in next entry.
Josip Mati?
Hello

I took a break from exams in order to work on the game. For now, I managed to pass Databases exam and Intro to Computer Science exam so I'm a little tired from studying.

[sharedmedia=gallery:images:4044]

This time, there are no any big updates on the progress, except for graphical revamp (which is still in progress). Instead, I'd like to share a current version of the game. I'd like to hear your feedback of any kind about the game. Part of this is for selfish reasons - I'm starting to get bored of it due to (somewhat) long production (burnout?); however I don't want to start a new project because I'll never finish this one. Another problem is that I don't know what I'll do with it once I finish it, other than add it to my portfolio. Apart from these concerns, I still enjoy making it.
Any suggestions, comments etc. are appreciated.

Grab the game:
Battle City.zip
and just extract and run.
Game requires .NET Framework 4.0 installed on the machine to run!

Controls: Up, Down, Left, Right, Enter (Shoot).

For now, I disabled 2 player mode and Options screen. Options screen is incomplete and 2 player mode has too many bugs I need to fix. For example, AI once took control over my tank and suicided it!!! I couldn't believe my eyes.

To change settings, open config.txt file. Parameters are easily recognizable; however, I didn't code in any protective mechanisms so if game suddenly starts crashing, check config file.

Currently known bugs:

  • 2 enemy tanks get stuck in small passages.
  • Sometimes enemy tanks go between two closest tiles.
  • Sometimes enemy tanks can fire a stream of bullets.
  • Brick wall destructions is sometimes too big or too small.
  • On widescreen resolution, mouse cursor and level editor cursor are displaced.

    Other than that, alpha is perfectly playable.
    One little note - in current state, game has 1897 lines of code (calculated with Visual Studio's code analysis), which also officialy makes it the biggest program I ever wrote.

    Thanks for reading.
Josip Mati?
Hello

Some time has passed from last update. Semester end is coming close and with it summer vacation, valuable time for learning anything programming-related and... Exams. Lots of exams. Oh how I don't want to study from faculty materials, I'd rather invest time on working on projects or study from real life examples where I can learn much faster and more efficient. Sadly, math and physics aren't courses which I can learn that way. At least I had good math professor and managed to pick up most from him; physics was bad so I need to learn by myself.

As far as development goes, AI is in satisfying condition so I left it for now in current state. Most problems have been fixed, there are some issues here and there, but it's playable. With that out of the way, I've turned to the next tasks:

  • Options screen - mostly complete; pure text, serves purpose
  • Powerup system
  • Graphical update

    I finally came to the powerups. This is where I can become creative and make major distinction between my clone and the original game. Apart from some powerups which are standard and thus picked up from the original, I can create some of my own as long as I know how to implement it. In preparation for implementation, I have to modify existing code in order to provide better interaction between objects. Hopefully, experience will be satisfying.
    There will be two major differences in powerup system between original and clone. One, powerups will be delivered "from the air" instead of appearing after destruction of certain enemy. Two, powerups won't disappear after lying for some time without collecting - after some time from appearance of an powerup, enemy tanks will be able to pick it up as well. Because of that, some powerups will have to have different effects depending on who picks it up. I can't wait to see how will this end up.

    Here's a list of the 12 powerups I plan to implement for start.
    [sharedmedia=gallery:images:4005]

    Now, back to the title. In order to get better assets, I've started to practice artistic skills. Because of its (relative) simplicity, I started with pixel art. Result can be seen on the picture above (I hope - resizing small sprite often brings catastrophe), with powerup icons, new brick wall and new tank. Any feedback would be appreciated.
    By using pixel art and vivid colors, I plan to give the game a retro feel suited to the NES and Sega Genesis/Megadrive games.

    Special shoutout and thanks to riuthamus for spending his own valuable time in order to help me with art - new tank is mostly his work, I just finished shading it. Without him, I would still be stuck with art skill on absolute zero; thanks to his help, I can produce something at least decent and continue learning from that. It's much harder than coding... time to make pencil, paper and eraser my best friends, huh?

    Thanks for reading
Josip Mati?
Hello

Well, I've been out for a while. There is a lot going on, my faculty exams are finished. Since I'm busy with faculty I simply don't have enough time to work on what I want - and even when I have I don't have enough motivation to do it.
Regarding faculty, this semester I have to write seminary - topic I managed to get is "Pathfinding algorithms". Fun fun.

For now, I didn't done much. I'm still working on the proper AI for the game. OK, not AI but pathfinding and pathfollowing. I've decided to work on A* algorithm, considering it's one of the easier and effective algorithms. However, I needed to rewrite it several times because... Code was a mess and it just didn't work. Current version is the closest to the what I want, but there are still several problems I'm trying to solve:

  • AI is blind to the changes. Once it finds a route it will follow it no matter what. I've had to remove collision from enemy tanks in order for them to not get stuck.
  • On several occasions, game stops for a little while AI calculates a route => inefficient pathfinding.
  • If I limit pathfinding to only few steps but make it check for paths each cycle, AI goes crazy.

    Definietly hardest piece of code yet I've had to write. And I feel it will need some time to write a satisfying solution.

    Also, in addition to that, I've been doing some cleanups of the code. Nothing special nor visible - removing of useless structures, replacing parts of code violating "Rule of 3" to accomodate DRY concept etc. - but will at least give me more flexibility later at writing the code. One of those changes is removing structure I've been using to save all points and instead using Player class to store all data connected to the players.

    Since I don't have any new pictures, I though I'd at least give a video of game in action.



    Little related to the game, May's article contest theme is "Remake the classics". Maybe I join in, considering that original Battle City can be considered "classic"; however, I don't have any idea what to write about, especially considering that my game is unfinished.

    Thanks for reading!
Josip Mati?
Hello again!

Here's another project status update; unlike last time, this time I made more "visible" stuff rather than tuning the code that operates it. Well... when I think about it... there is plenty of both. This update is rather big (for me) and I cannot express how determined and happy I am because of it.

Short summary:


  • Added level selection screen
  • Level progression, scoring and life counting
  • Game over and Victory mechanics
  • New types of tiles (20 of them)
  • Improved collision detection and destruction
  • Complete level editor
  • Manual (by editing config file) resolution selection
  • Framerate counter

    New Collision Detection with destructables



    Remember the solution I used in last journal entry for destroying brick walls? If you don't, let me help you: you don't need to. It's gone. Simpler, better and faster solution was implemented instead which gives me greater control over tiles while doing the exactly same thing as pixel-perfect collision detection (PPCD in short) - tile is splitted into multiple segments which are checked if CD detects there is collision with that tile.
    There were also performance issues with PPCD on levels where were many bricks which caused framerate drops from 60 FPS to 38 due to multiple checking, transforming etc. due to nature of the code. Maybe I didn't write it perfectly optimized but still, calling 40 000 times same operation is not healthy.

    New CD also helped me to create new types of tiles easily, which brings us to...

    Level Editor



    2j35xqd.jpg

    Oh yeah, complete level editor with toolbox (took inspiration from Sapphire Yours) and little menu with all necessary operations. Level editor is operated by mouse (currently only place where mouse is enabled). In the toolbox you can also see all tiles which currently exist in the game; numerous variations of brick and metal tile were easy to implement thanks to new collision detection. Save and Load Level bring out dialog box where you write which level you want to load or save. Those two will be improved later, together with warning there are unsaved changes when you try to exit the editor, additional level management features and few more safety measures like prevention of saving empty level.

    Since level editor exists, there is also level selection screen:
    34yxiza.jpg
    It's ugly. I know. But it's "similar" to the original (NES) version and will suffice for now. Also, game automatically transitions between levels when you kill all enemy tanks through this screen; when it does that, player is prevented from choosing other level.

    Configuration file

    For easier manipulation over destructibles (read as: I don't wanna mess with non-integers if I don't need to) resolution the game is based is increased from 800*600 to 960*720. Naturally, it caused few minor issues I didn't fix yet (respawn control going berserk, font a little smaller). Game doesn't look much different, except for fresh stuff on the right side:

    bdjnt2.jpg

    What happens when you change resolution? For 4:3 resolutions (like 1280*1024) everything's same except bigger. For non-4:3 resolutions, this happens:

    1zwj6lx.jpg

    Screenshot is taken on HDReady resolution (1366*768). On this picture you can notice 3 things.
    First one is a little gray part on the left side which "came out of nowhere". I didn't like horizontal stretching during expansion on 16:9 format so I rather kept the main part in 4:3 format and added more background part to the left, keeping nice square tiles.
    For now, resolution is selected by manually editing configuration file. Aside from height and width, there are also Fullscreen and Show Framerate options. I have 32' TV as my monitor screen and game in current format looks really nice on 1920*1080 fullscreen.

    Game over, Victory and game exiting



    What is on of main components which makes the game "game"? Challenge. Or rather, ability to lose. Now, on the previous picture, look at the bottom right of the game screen. "Game Over" for 2nd player. Yup, it's finally properly supported. I don't need to explain rules, right? smile.png

    I took advantage of Game State Machine to make this little thing that You probably spotted first on that screenshot. I think I don't need to talk about it.

    What is next?



    Obviously, to raise up the challenge, I need to improve AI of opponents. For now, it utilizes randomizer to create its behaviour which gives little challenge, but I swear that Random Number Generator has its own mind. Luck my ***, there is no other explanation on AIs ability to dodge perfectly fired shots in last second; if I didn't write it myself I would swear that AI tries to sabotage me.
    I already have designed the behaviour of new AI, now I "just" need to implement it properly. CPU overload, here I come!

    I really don't know what will I do with art and sound. I tried to draw new tank for Player 1 to replace the current one but the result was catastrophic due to overusage of Bevel&Emboss to create a 3D feeling:
    [spoiler]
    nbwzdt.png
    It's ugly. Especially when you shrink it on 48*48.
    [/spoiler]

    I'll try to get new assets as soon as I can but until then I'll work on other things like improving code and performance and adding support for playing in 2 players over LAN.

    Well, this is it for now, thanks for reading, I hope I wasn't too boring.
Josip Mati?
Winter vacation is over sad.png
First things first: I know that it's late, but Happy New Year! Hope you had a good vacation.
I didn't have much time to invest into project due to all the events surrounding Christmas and New Year so there was another 2 week "rest" from programming. In the evenings I'd rather play a little or watch movies/animes since it would be pretty late for work and I would be mentally and physically exhausted.

Well, I'd like to say that my project is going steadily, but it isn't. It's going in bursts. After finishing as much as I could, I scrapped previous build and wrote a new one, drastically improving it by using structures I never used before (believe it or not, Lists are one of those) and going deeper into object oriented programming. None of my programs before never had more than 3 classes written by myself; current version of Battle City has 16 classes and will gain more. So, while visually it changed little, core of it now works much better than before.

This is how it looks currently:
[sharedmedia=gallery:images:3156]
On the left side: somewhat bland main menu. On the right side, screenshot of 2 player game featuring destructible walls.

Some major features I tackled since my last entry:

Game State Manager



In other words, multiple screens. This system was on the whole different level than I ever wrote before and it took considerable amount of time just to study how it works. In addition to that, I needed to change whole my thought process to be able to grasp the concept. Microsoft's Game State Manager example was a great help in research; I managed to reproduce my own version by looking carefully how that example works.

As a result, game got Main Menu. Yaaay \o/ Other screens (like disabled level editor you can see on screenshot) will be simple to add thanks to it.

Destructible brick walls



This was a little problematic... mainly because I knew "what" and "how" to do it, but didn't know how to properly execute that "how".
First time I tried, I managed to break the game by calling 2 lines of code 16 million times, resulting in several second freeze each time I touched brick wall with my tank. I managed to execute the solution correctly on my 2nd try (meaning I deleted completely that part once and rewrote it from scratch) and got great results.

While writing the algorithm I learned the basics of transformation matrixes - utility which will be required later, especially in 3D projects.

Solution was to go pixel per pixel in area where tank/bullet overlaps with wall, find that pixel's coordinates in both the tank's/bullet's and wall's texture using transformation matrixes and check if it's transparent by looking at alpha parameter. Since checking each pixel is insanely resource consuming, collision must first be confirmed by bounding box test.

Game finally got some feeling like the one I'm trying to clone.

Funny bugs

(that may come later as "features", because that's what experts do)



It's easy to stumble on few unforseen bugs, especially when one is as inexperienced as I am. Some of them are usual, other of them... so funny or cool looking that they beg you to stay. I wanted to share some of them before I forget; sadly, I don't have videos of them and pictures aren't enough in this case.

So, here they are:

- Star-powered tank
[indent=1]* Description: Player tank is invulnerable, ramming into enemy tanks kills them

[indent=1]* Status: Resolved

[indent=1]* Cause: Forgotten conditions when evaluating collision between two tanks


- Ghost tank

[indent=1]* Description: After player loses all lives, he respawns looking like enemy tank. Ghost tank can move and shoot and is invulnerable.

[indent=1]* Status: Put aside (old version of the game)

[indent=1]* Cause: Oversight on tank respawn involving player tanks

- Death by Spawning

[indent=1]* Description: Enemy tank spawns while player's tank is on spawn point and immediately fires bullet, killing player. Reverse also works.
[indent=1]* Status: Resolved
[indent=1]* Cause: Non-existant spawn control

- Unstable tank

[indent=1]* Description: Player's tank dies when touching environment. Respawns immediately on spawn.
[indent=1]* Status: Resolved
[indent=1]* Cause: Copied code from one property but forgot to change it.

- Tank possesion

[indent=1]* Description: When player dies, instead of respawning he assumes control of one of AI controlled tank. In case of multiplayer, Player 1 gains control of Player 2's tank while Player 2 gains control of enemy tank.
[indent=1]* Status: Resolved
[indent=1]* Cause: Due to Player 1 controls being hardcoded to index 0 of the list of tanks and Player 2 - if it's multiplayer - being hardcoded to index 1, when Player 1's tank is removed from list (killed) other tank's indexes are dropping by 1. You can guess what happens next.

As you can see, causes for those bugs are pretty trivial. Regardless of that, I love when bugs like that happen, giving me unexpected bursts of laugher.

Next step