Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


slayemin

Member Since 23 Feb 2001
Offline Last Active Dec 22 2014 01:04 PM

#5199044 Where should I start learning game development?

Posted by slayemin on 18 December 2014 - 08:47 PM

You can also check out Unreal Engine 4. They have a special student license so it will help you with learning without paying.

 

UE4 comes with a bunch of starter content and a bunch of documentation and tutorials, so you can literally place a textured cube into a game world in less than a minute. There are some nice video tutorials which go through how to build a house, step by step, and wire it up with blueprints to get some interactivity going (ie, push a button to open a door). No code required!




#5198911 Is Java a good Language for Games?

Posted by slayemin on 18 December 2014 - 03:20 AM

Short answer: Yes.

 

Java is a tool (like a paint brush). A tool is only as good as the person using it.

 

Bad artists will make bad art, regardless of how good their tools are.

 

Good artists will make good art, regardless of how bad their tools are.

 

Sensible artists will make amazing art with the best tools they can get their hands on, but their tools won't limit what they can and can't do.

 

I mean, Rollercoaster tycoon was written in Assembly. That's insane. But the developers still made a great game. There's tons of free game engines out there and people still make terrible games with them.




#5198910 Need Guidance and Advice

Posted by slayemin on 18 December 2014 - 03:15 AM

1) Personally, if I was in your position, I'd keep my job as a financial adviser. You can do your job and make lots of money and just play games in your free time. Making games is entirely different from playing them and not nearly as financially rewarding.

2) If you want to get into game development anyways and lift the curtain behind it all, here is what I'd recommend:

IF you want to be a programmer:
Get good at mathematics. Get proficient at everything up to and including Calculus and linear algebra. The cartesian coordinate system will be something you use every day. Start super simple. Pace yourself. And most importantly, be dedicated. Don't give up when things get hard and frustrating. Push hard to overcome all of the challenges you'll be faced with, because there will be many. There are tons of resources online to help you get started, so the internet is your oyster...

IF you want to be an artist:
Get good at drawing. Draw till your hands hurt and your room is full of art. Don't just fill your room with trash, try to improve. Get familiar with the digital tools used by most artists (Photoshop, Maya, Mudbox, X Normal, etc). Get smart with materials and shaders.

 

IF you want to be a producer:
Read up on PMP and start figuring out how you can put what you read into practice. Learn about the unforgiving iron triangle of money, features and time and how you need to balance it all out.

 

IF you want to be a game designer:
Hah. Good luck. Everyone wants to be a game designer. But, legitimately, you're going to have to design a lot of games and prove you know what you're doing. A designer will have to work quite hard to figure out how to balance the game, come up with interesting game mechanics, design levels, etc. It's harder than the mystique suggests.

 

If you want to be a game tester:
Its a pretty good foot in the door job with less technical skills required (just write well and be analytical). It's not a fun job though. You play the same god damned game over and over again for 8+ hours a day, doing the same things and trying to break the game and then documenting how you broke it and what happened. It gets tedious quickly and you have to test games you may have no interest in ever playing (unicorn adventures III anyone?).




#5198906 My Game Plan

Posted by slayemin on 18 December 2014 - 02:53 AM

C# is a good starting point.

 

Keep in mind, you're going to be learning two things at the same time:
1) How to write C# code
2) How to write a program using logical instructions

The two are not the same. You can accomplish #2 in any language, but the means you're using in this case is via C#. So, when you're struggling to make something work, you need to be careful to ask the question: "Is this a language syntax issue or an issue with trying to figure out how to correctly write instructions sequentially?"

 

So, with #2, you can actually accomplish this by writing your instructions in english using pseudo-code, such as "Add two to the value X." or "Turn the space ship by 45 degrees and then apply the thruster."

Why is this important? Because you're going to eventually get to a point where you say, "How do I write a for loop in language XYZ which iterates over ten items?". The end result is the same (logically speaking) but implemented differently depending on which language you are implementing it in. So, "programming" is a language independent, abstract discipline, in which you get good at it through lots of indirect practice.

When you're just getting starting with making *any* type of program, you want to start SUPER small and simple. Start with printing the classic "Hello World" onto the screen. Then create a "Guess my number" type game, where the computer picks a random number and you have a fixed number of tries to guess it. Then, create tic-tac-toe. Then try creating something more complicated, like break out. Graduate to tetris. Then maybe pac man or some other old school atari games. Stick to 2D until you get really familiar with the game development process. When / if you eventually shift to 3D, your literally adding an additional dimension and the amount of work grows in an order of magnitude and the complexity increases equally (ie, you can figure out what someone is clicking on very easily in 2D, but in 3D you need to shoot a ray out into the game world and intersect it with a 3D object).

When things get difficult, I find what helps the best is to try to write out what I'm doing in pseudo-code and work out the solution to the problem on a hand-held whiteboard. If I can't solve the problem outside of code, what hope would I have of solving the problem with the additional complexity of code?

Also: If you *really* want to get good at writing code, get good at mathematics! Get good at trigonometry, linear algebra, algebra, etc. You'll be relying on the strength of your math skills every day. You don't want your math skills to be your lowest common denominator which limits your capabilities. Getting good at math should also directly help you get better at writing and conceptualizing good code. for example, if you know the quadratic equation and you want to write a function to solve it, you already know what the objective is and what the answers should be when you test them, and you should be able to break the algorithm down into its component steps and verify that the values are the expected values. This is essentially what all programming boils down to: Do the steps make sense? Do the values match the expected values? Does the final product match the expected product? Now, put it all together to build a program which appears to do magic!
 




#5198899 0 experience in programming and game development

Posted by slayemin on 18 December 2014 - 02:23 AM

Well, im finishing school this year and i am leaned to begin a career as a game dev, i really like animations, modeling, cinematics, etc. but i never tried making any of these, also i have 0 experience in programming, i love playing games and thats why im choosing to start in this trade.

 

Please guys, advise me and give me tips to begin learning before starting university with 0 experience.

 

Thanks!

 

P.S: im bad at drawing sad.png

If you like animations, modeling and cinematics, and that's what you want to get into, then you need to get good at producing those types of work to a high degree of quality. That means you need to get good at producing art. The fact that you aren't good at *drawing* is concerning, though not a show stopping problem. Get better at it through practice!

Game programming has very little to do with the artistic side. The closest you'll get in programming to artwork is becoming a graphics programmmer, and most of that is more science related than art related (such as figuring out how light interacts with surfaces of different materials and shapes and distilling that into mathematical formulas).

My best recommendation is to pick up the book titled "Game Plan". It gives you a detailed synopsis of all of the various roles necessary to building and shipping a game. The roles aren't just limited to art and programming, there are tons of other roles and needs which need to be filled by people who know what they're doing! Check out the book and find the role which aligns most closely with your interests and skill set, then get better at that.

One thing to keep in the back of your mind: Video games are software. Game development is a subset of software development. So, getting good at various parts of the software development process will make you valuable as a game developer, and when you are tired of making games (which may happen based on industry statistics), you can move on to other sectors of the software development industry. You have a fall back plan for financial and career security!




#5192738 Corrupting of the view if far from world center

Posted by slayemin on 13 November 2014 - 04:48 PM

Yep, its almost certainly caused by floating point precision errors. The farther from 0 a value gets, the less precise a float gets. I think the step size between float values increases exponentially as you get further and further away from zero.

 

With your airplane model, what ends up happening is that the calculated vertex positions hit on a value which is between the ranges of a float, so the float just truncates the value? (or it might round?). ie, 9.00000000001 would fall between the value 9.0 and 9.0000000000234, and just become 9.0. The visual effect is that the airplane model verticies get mushed together and the end result is something that looks like a bad low-poly version of the plane.

 

What's the fix?

A) try to keep your floating point values near 0 because the step values are very close

B) use doubles to get more precision on higher numbers (Though you're just kicking the problem farther down the road)

 

Techniques & options:

A) keep the camera at 0 (as mentioned above) and move the world around the camera, easiest solution with least complexity

B) At certain fixed distances from 0, translate everything in the world to recenter on zero (ie, at 10k, shift everything by 10k to get back to 0). This can get tricky and cause bugs.

C) Just use smaller worlds in your design

D) Scale the world down




#5191387 XML vs Database

Posted by slayemin on 05 November 2014 - 01:24 PM

If you need multiple users to connect to and access data which may change at any time, use a database.

 

If you need to protect the integrity of your data and have a way to update it across the board for all users, use a database.

 

If you don't care about players tampering with the game data stored on their local disk (ie, it's their fault if they break the game), then you can just store the config/data files on their local machines and load it into memory when the game loads.

 

 

When it comes to storing the data, whether you use XML or a SQL table, you'll need to figure out some way to map the stored data to your internal class variables. I can't really tell you how to do this since it's very dependent on how you want to manage your data. Generally, what I do is create two methods in each class which needs to be persistent, and each method is responsible for saving and loading its state to some location.

Class MyGameObject
{
    int myValue = 5;

    public void SaveObject(string path)
    {
         //saves myValue to disk by writing "5" to the file in the given path
         SaveValueToFile(path, myValue);
    }

    public void LoadObject(string path) //<-- could also be a constructor arg
    {
        //loads the value from disk and sets it
        int diskValue = GetValueFromFile(path);
        
        //sanity check on the loaded value
        if(diskValue < 100 && diskValue >= 0)
            myValue = diskValue;
        else
            myValue = 5;
    }

}



#5191381 Resource management

Posted by slayemin on 05 November 2014 - 01:04 PM

I implemented pretty much what you're describing.

* It tries to keep a reference counter on each resource. When it reaches zero, I have the option to unload the resource from memory. I'm not really using this feature yet because I haven't needed to.

* I don't actually load an asset into memory until it's actually needed. I just store the disk path to the asset. When an object calls the "get" method for the asset, it will pull it from disk and put it into memory if its not in memory yet.

* Users of the class interact with their assets by giving them a name. They can get an object by name. The integer ID is used on the backend, but is very unfriendly for people. I ended up asking "okay, what texture do I get when I ask for ID# 23? I have no idea!"

* My asset database is used as a singleton within another class, though there's no reason you can't have multiple asset managers.
* I use separate content datastructures for each asset type, so one for textures, one for models, one for effects, one for fonts, etc. There's no reason assets can't use the same name so long as they're different asset types. This may make it easier for the user to manage their bundled assets, ie, "GetTexture("Peasant"), GetModel("Peasant"), GetEffect("Peasant")"

 

 

Here is my complete implementation class (~950 lines of code):

#define DEBUG_MODE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace EricsLib.ContentDatabases
{
    /// <summary>
    /// This is the asset database for storing and managing all assets being used within the game.
    /// </summary>
    /// <example>
    /// Usage:
    /// AssetDB m_db = new AssetDB();
    /// m_db.AddTexture("Textures\\Grass", "Grass");        //insertion
    /// Texture2D grassTexture = m_db.GetTexture("Grass");  //retrieval
    /// m_db.ReleaseTexture("Grass");                       //release handle
    /// </example>
    public class AssetDB
    {
        /*Implemented feature: We keep an internal database for asset tracking. If the asset hasn't been used for a long time or the asset is no longer being
         used by any objects, we can release the main memory of the asset. If the asset is requested later on, we can load it back into memory. This feature would
         allow us to have a smaller memory footprint.
         
         Optional Feature: We could use multi-threading to load external assets into and out of memory so that the main game doesn't have to wait for an asset to be loaded.
         */
        #region Fields
        struct _Texture
        {
            public Texture2D asset;
            public int refCount;
            public string path;
            public bool loaded;

            /// <summary>
            /// Creates a new texture object
            /// </summary>
            /// <param name="AssetPath">The relative content path to the asset</param>
            public _Texture(string AssetPath)
            {
                asset = null;
                refCount = 0;
                loaded = false;
                path = AssetPath;
            }

            /// <summary>
            /// This unloads the asset but keeps the path in case we want to reload it later.
            /// </summary>
            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            /// <summary>
            /// This loads the asset into main memory by using the given content manager.
            /// </summary>
            /// <param name="content">the content manager to use for loading the asset</param>
            public void Load(ContentManager content)
            {
                asset = content.Load<Texture2D>(path);
                loaded = true;
                refCount = 0;   //just because we've loaded the asset doesn't guarantee that its being used...
            }
        }

        struct _Model
        {
            public Model asset;
            public int refCount;
            public string path;
            public bool loaded;
            public BoundingBox boundingBox;
            public BoundingSphere boundingSphere;

            public _Model(string Path)
            {
                asset = null;
                refCount = 0;
                path = Path;
                loaded = false;
                boundingBox = new BoundingBox();
                boundingSphere = new BoundingSphere();
            }

            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            public void Load(ContentManager content)
            {
                asset = content.Load<Model>(path);
                loaded = true;
                refCount = 0;
                CalculateBounds();
            }

            public BoundingBox CalculateBounds()
            {
                if (loaded)
                {
                    List<Vector3> vertList = new List<Vector3>();
                    foreach (ModelMesh mm in asset.Meshes)
                    {
                        foreach (ModelMeshPart mmp in mm.MeshParts)
                        {
                            VertexBuffer vb = mmp.VertexBuffer;
                            Vector3[] data = new Vector3[vb.VertexCount];
                            vb.GetData<Vector3>(data);
                            vertList.AddRange(data);
                        }
                    }
                    boundingBox = BoundingBox.CreateFromPoints(vertList);
                    boundingSphere = BoundingSphere.CreateFromPoints(vertList);
                }

                return boundingBox;
            }
        }

        struct _Effect
        {
            public Effect asset;
            public int refCount;
            public string path;
            public bool loaded;

            public _Effect(string AssetPath)
            {
                asset = null;
                refCount = 0;
                path = AssetPath;
                loaded = false;
            }

            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            public void Load(ContentManager content)
            {
                asset = content.Load<Effect>(path);
                asset.Name = path;
                loaded = true;
                refCount = 0;
            }
        }

        struct _Font
        {
            public SpriteFont asset;
            public int refCount;
            public string path;
            public bool loaded;

            public _Font(string AssetPath)
            {
                asset = null;
                refCount = 0;
                path = AssetPath;
                loaded = false;
            }

            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            public void Load(ContentManager content)
            {
                asset = content.Load<SpriteFont>(path);
                loaded = true;
                refCount = 0;
            }
        }

        /* The readonly keyword is different from the const keyword. A const field can only be initialized at the declaration of the field. 
         * A readonly field can be initialized either at the declaration or in a constructor. Therefore, readonly fields can have different 
         * values depending on the constructor used. Also, while a const field is a compile-time constant, the readonly field can be used 
         * for runtime constants.
         * See: http://msdn.microsoft.com/en-us/library/acdd6hb7.aspx
         */

        //The data is keyed by integers
        readonly Dictionary<int, _Texture> m_textureDB;
        readonly Dictionary<int, _Model> m_modelDB;
        readonly Dictionary<int, _Effect> m_effectDB;
        readonly Dictionary<int, _Font> m_fontDB;

        

        //these are lookup keys which can be used to find the internal key 
        Dictionary<string, int> m_textureLookup;
        Dictionary<string, int> m_modelLookup;
        Dictionary<string, int> m_effectLookup;
        Dictionary<string, int> m_fontLookup;

        int m_textureIndex;
        int m_modelIndex;
        int m_effectIndex;
        int m_fontIndex;

        ContentManager m_content;
        #endregion

        #region Initialization

        public AssetDB(ContentManager content)
        {
            m_content = content;

            m_textureIndex = 0;
            m_modelIndex = 0;
            m_effectIndex = 0;
            m_fontIndex = 0;

            m_textureDB = new Dictionary<int, _Texture>();
            m_modelDB = new Dictionary<int, _Model>();
            m_effectDB = new Dictionary<int, _Effect>();
            m_fontDB = new Dictionary<int, _Font>();

            m_textureLookup = new Dictionary<string, int>();
            m_modelLookup = new Dictionary<string, int>();
            m_effectLookup = new Dictionary<string, int>();
            m_fontLookup = new Dictionary<string, int>();

            CreateSolidTexture();
            CreateDebugTexture();
        }
        #endregion

        #region Texture Interface

        /// <summary>
        /// There's no guarantee that your user will know to add a solid texture, so let's create it dynamically.
        /// </summary>
        private void CreateSolidTexture()
        {
            Texture2D solid = new Texture2D(BaseSettings.Graphics, 128, 128);
            int size = 128*128;
            Color[] col = new Color[size];

            for (int a = 0; a < size; a++)
                col[a] = Color.White;

            solid.SetData<Color>(col);

            if (!m_textureLookup.ContainsKey("Solid"))
            {
                _Texture tmp = new _Texture();
                tmp.asset = solid;
                tmp.loaded = true;
                tmp.refCount = 1;

                m_textureLookup.Add("Solid", m_textureIndex);
                m_textureDB.Add(m_textureIndex, tmp);
                m_textureIndex++;
            }
        }

        private void CreateDebugTexture()
        {
            //Rather than throwing exceptions when a texture doesn't exist, we just present this one!

            const int SIZE = 256;
            Texture2D solid = new Texture2D(BaseSettings.Graphics, SIZE, SIZE);
            int size = SIZE * SIZE;
            Color[] col = new Color[size];


            for (int y = 0; y < SIZE; y++)
            {
                int fy = (int)(y / 32f) % 2;
                for (int x = 0; x < SIZE; x++)
                {
                    int fx = (int)(x / 32f) % 2;
                    if (fy == 0)
                    {
                        if (fx == 0)
                            col[y * SIZE + x] = Color.Gray;
                        else
                            col[y * SIZE + x] = Color.Magenta;
                    }
                    else
                    {
                        if (fx == 0)
                            col[y * SIZE + x] = Color.Magenta;
                        else
                            col[y * SIZE + x] = Color.Gray;
                    }
                }
            }

            solid.SetData<Color>(col);

            if (!m_textureLookup.ContainsKey("Invalid"))
            {
                _Texture tmp = new _Texture();
                tmp.asset = solid;
                tmp.loaded = true;
                tmp.refCount = 1;

                m_textureLookup.Add("Invalid", m_textureIndex);
                m_textureDB.Add(m_textureIndex, tmp);
                m_textureIndex++;
            }
        }

        public bool HasTexture(string Handle)
        {
            return m_textureLookup.ContainsKey(Handle);
        }

        public bool HasTexture(int TextureID)
        {
            return m_textureDB.ContainsKey(TextureID);
        }

        /// <summary>
        /// Gets the requested texture from the database, provided you've added a texture with the given handle
        /// </summary>
        /// <param name="AssetName">The handle to the texture</param>
        /// <returns>The requested texture</returns>
        /// <remarks>This will load the texture from disk into main memory if it has not been loaded already.</remarks>
        public Texture2D GetTexture(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                return GetTexture(m_textureLookup[AssetName]);
            }
            else
            {
                #if DEBUG_MODE
                throw new Exception("GetTexture(): " + AssetName + " key doesn't exist in the database");
                #else
                return GetTexture(m_textureLookup["Invalid"]);
                #endif
            }
        }

        public Rectangle GetTextureSize(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                Texture2D tmp = GetTexture(m_textureLookup[AssetName]);
                return new Rectangle(0, 0, tmp.Width, tmp.Height);
            }
            else
            {
#if DEBUG_MODE
                throw new Exception("GetTextureSize(): " + AssetName + " key doesn't exist in the database.");
#else
                Texture2D tmp = GetTexture(m_textureLookup["Invalid"]);
                return new Rectangle(0, 0, tmp.Width, tmp.Height);
#endif
            }
                
        }


        /// <summary>
        /// Gets the texture.
        /// </summary>
        /// <param name="TextureID">The texture identifier.</param>
        /// <returns>Texture2D.</returns>
        /// <exception cref="System.Exception">GetTexture():  + TextureID +  key doesn't exist in the database</exception>
        public Texture2D GetTexture(int TextureID)
        {
            if (m_textureDB.ContainsKey(TextureID))
            {
                _Texture tmp = m_textureDB[TextureID];

                if (tmp.loaded)
                    return m_textureDB[TextureID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_textureDB[TextureID] = tmp;
                    return tmp.asset;
                }
            }
            else
            {
#if DEBUG_MODE
                throw new Exception("GetTexture(): " + TextureID + " key doesn't exist in the database");
#else
                return GetTexture(m_textureLookup["Invalid"]);
#endif
            }
                
        }

        /// <summary>
        /// Gets the texture identifier.
        /// </summary>
        /// <param name="AssetName">Name of the asset.</param>
        /// <returns>The ID used to reference the texture.</returns>
        /// <remarks>Returns -1 if the asset doesn't exist.</remarks>
        public int GetTextureID(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
                return m_textureLookup[AssetName];
            else
                return m_textureLookup["Invalid"];
        }

        /// <summary>
        /// Inserts a texture object into the database.
        /// </summary>
        /// <param name="AssetPath">The path to the texture asset within your content project</param>
        /// <param name="AssetName">The name you want to use to access your texture</param>
        public void AddTexture(string AssetPath, string AssetName)
        {
            if (!m_textureLookup.ContainsKey(AssetName))
            {
                _Texture tmp = new _Texture(AssetPath);
                
                m_textureLookup.Add(AssetName, m_textureIndex);
                m_textureDB.Add(m_textureIndex, tmp);
                m_textureIndex++;
            }
            else
            {
                //the texture already exists, so just increment its reference counter
                _Texture tmp = m_textureDB[m_textureLookup[AssetName]];
                tmp.refCount += 1;
                m_textureDB[m_textureLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Inserts a texture object into the database
        /// </summary>
        /// <param name="AssetPath">The path within your content project to the texture asset. Note: The filename will be the asset name.</param>
        public void AddTexture(string AssetPath)
        {
            string[] tmp = AssetPath.Split('\\');
            string AssetName = tmp[tmp.Length];

            AddTexture(AssetPath, AssetName);
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this texture is no longer used by any objects, the texture data is released from main memory.
        /// However, the meta data still exists. If you need to access the texture again, the texture will be
        /// reloaded from disk.
        /// </summary>
        /// <param name="AssetName">The asset lookup key</param>
        public void ReleaseTexture(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                _Texture tmp = m_textureDB[m_textureLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                    tmp.refCount -= 1;

                m_textureDB[m_textureLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a texture from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the texture name</param>
        public void DeleteTexture(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                m_textureDB.Remove(m_textureLookup[AssetName]);
                m_textureLookup.Remove(AssetName);
            }
        }

        #endregion

        #region Model Interface
        public bool HasModel(string AssetName)
        {
            return m_modelLookup.ContainsKey(AssetName);
        }

        public bool HasModel(int AssetID)
        {
            return m_modelDB.ContainsKey(AssetID);
        }

        /// <summary>
        /// Gets the model.
        /// </summary>
        /// <param name="AssetID">The asset identifier.</param>
        /// <returns>Model.</returns>
        /// <exception cref="System.Exception">Key doesn't exist in the database</exception>
        public Model GetModel(int AssetID)
        {
            if (m_modelDB.ContainsKey(AssetID))
            {
                _Model tmp = m_modelDB[AssetID];

                if (tmp.loaded)
                    return m_modelDB[AssetID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_modelDB[AssetID] = tmp;
                    return tmp.asset;
                }
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        /// <summary>
        /// Gets the model.
        /// </summary>
        /// <param name="AssetName">Name of the asset.</param>
        /// <returns>Model.</returns>
        /// <exception cref="System.Exception">Model doesn't exist in the database</exception>
        public Model GetModel(string AssetName)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                return GetModel(m_modelLookup[AssetName]);
            }
            else
                throw new Exception("GetModel(): " + AssetName + " Model doesn't exist in the database");
        }

        public Model GetModel(string AssetName, ref BoundingBox boundingBox, ref BoundingSphere boundingSphere)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                int ID = m_modelLookup[AssetName];
                if (m_modelDB.ContainsKey(ID))
                {
                    _Model tmp = m_modelDB[ID];

                    if (tmp.loaded)
                    {
                        boundingBox = m_modelDB[ID].boundingBox;
                        boundingSphere = m_modelDB[ID].boundingSphere;
                        return m_modelDB[ID].asset;
                    }
                    else
                    {
                        tmp.Load(m_content);
                        tmp.refCount += 1;
                        m_modelDB[ID] = tmp;

                        boundingBox = m_modelDB[ID].boundingBox;
                        boundingSphere = m_modelDB[ID].boundingSphere;

                        return tmp.asset;
                    }
                }
                else
                {
                    throw new Exception("GetModel(): " + AssetName + " Model doesn't exist in the database");
                }
            }
            else
                throw new Exception("GetModel(): " + AssetName + " Model doesn't exist in the database");
        }

        public void AddModel(string AssetPath, string AssetName)
        {
            if (!m_modelLookup.ContainsKey(AssetName))
            {
                _Model tmp = new _Model(AssetPath);

                m_modelLookup.Add(AssetName, m_modelIndex);
                m_modelDB.Add(m_modelIndex, tmp);
                m_modelIndex++;
            }
            else
            {
                //the texture already exists, so just increment its reference counter
                _Model tmp = m_modelDB[m_modelLookup[AssetName]];
                tmp.refCount += 1;
                m_modelDB[m_modelLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Changes the model associated with an asset name
        /// </summary>
        /// <param name="AssetName">The name you want to use to reference the asset</param>
        /// <param name="model">The model object you want to change</param>
        public void UpdateModel(string AssetName, Model model)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                _Model tmp = m_modelDB[m_modelLookup[AssetName]];
                tmp.asset = model;
                m_modelDB[m_modelLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this asset is no longer used by any objects, it is deleted.
        /// </summary>
        /// <param name="AssetName">The asset lookup key</param>
        public void ReleaseModel(string AssetName)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                _Model tmp = m_modelDB[m_modelLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                    tmp.refCount -= 1;

                m_modelDB[m_modelLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a model from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the model name</param>
        public void DeleteModel(string AssetName)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                m_modelDB.Remove(m_modelLookup[AssetName]);
                m_modelLookup.Remove(AssetName);
            }
        }
        #endregion

        #region Effect Interface

        /// <summary>
        /// Determines whether the effect database has the effect.
        /// </summary>
        /// <param name="AssetID">The asset identifier.</param>
        /// <returns><c>true</c> if the specified asset identifier has effect; otherwise, <c>false</c>.</returns>
        public bool HasEffect(int AssetID)
        {
            return m_effectDB.ContainsKey(AssetID);
        }

        /// <summary>
        /// Determines whether the effect database has the effect.
        /// </summary>
        /// <param name="AssetName">Name of the asset.</param>
        public bool HasEffect(string AssetName)
        {
            return m_effectLookup.ContainsKey(AssetName);
        }

        /// <summary>
        /// Gets an effect by the asset ID
        /// </summary>
        /// <param name="AssetID">The asset identifier.</param>
        /// <returns>Effect.</returns>
        /// <exception cref="System.Exception">Key doesn't exist in the database</exception>
        public Effect GetEffect(int AssetID)
        {
            if (m_effectDB.ContainsKey(AssetID))
            {
                _Effect tmp = m_effectDB[AssetID];

                if (tmp.loaded)
                    return m_effectDB[AssetID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_effectDB[AssetID] = tmp;
                    return tmp.asset;
                }
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        /// <summary>
        /// Gets the requested asset from the database, provided you've added a asset with the given handle
        /// </summary>
        /// <param name="AssetName">The handle to the asset</param>
        /// <returns>The requested asset</returns>
        /// <remarks>This will load the asset into main memory if it has not been loaded.</remarks>
        public Effect GetEffect(string AssetName)
        {
            if (m_effectLookup.ContainsKey(AssetName))
            {
                return GetEffect(m_effectLookup[AssetName]);
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        /// <summary>
        /// Inserts a asset object into the database.
        /// </summary>
        /// <param name="AssetPath">The path to the asset asset within your content project</param>
        /// <param name="AssetName">The name you want to use to access your asset</param>
        public void AddEffect(string AssetPath, string AssetName)
        {
            if (!m_effectLookup.ContainsKey(AssetName))
            {
                _Effect tmp = new _Effect(AssetPath);
                m_effectLookup.Add(AssetName, m_effectIndex);
                m_effectDB.Add(m_effectIndex, tmp);
                m_effectIndex++;
            }
            else
            {
                _Effect tmp = m_effectDB[m_effectLookup[AssetName]];
                tmp.refCount += 1;
                m_effectDB[m_effectLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this asset is no longer used by any objects, it is released from main memory.
        /// </summary>
        /// <param name="Handle">The asset lookup key</param>
        public void ReleaseEffect(string AssetName)
        {
            if (m_effectLookup.ContainsKey(AssetName))
            {
                _Effect tmp = m_effectDB[m_effectLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                {
                    tmp.refCount -= 1;
                }

                m_effectDB[m_effectLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a asset from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the asset name</param>
        public void DeleteEffect(string AssetName)
        {
            if (m_effectLookup.ContainsKey(AssetName))
            {
                m_effectDB.Remove(m_effectLookup[AssetName]);
                m_effectLookup.Remove(AssetName);
            }
        }
        #endregion

        #region Font Interface

        public bool HasFont(string AssetName)
        {
            return m_fontLookup.ContainsKey(AssetName);
        }

        public bool HasFont(int FontID)
        {
            return m_fontDB.ContainsKey(FontID);
        }

        public int GetFontID(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
                return m_fontLookup[AssetName];
            return -1;
        }

        /// <summary>
        /// Gets the requested asset from the database, provided you've added a asset with the given handle
        /// </summary>
        /// <param name="AssetName">The handle to the asset</param>
        /// <returns>The requested asset</returns>
        /// <remarks>This will load the asset into main memory if it has not been loaded.</remarks>
        public SpriteFont GetFont(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
            {
                return GetFont(m_fontLookup[AssetName]);
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        public SpriteFont GetFont(int FontID)
        {
            if (m_fontDB.ContainsKey(FontID))
            {
                _Font tmp = m_fontDB[FontID];

                if (tmp.loaded)
                    return m_fontDB[FontID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_fontDB[FontID] = tmp;
                    return tmp.asset;
                }
            }
            else
                return null;
        }

        /// <summary>
        /// Inserts a asset object into the database.
        /// </summary>
        /// <param name="AssetPath">The path to the asset asset within your content project</param>
        /// <param name="AssetName">The name you want to use to access your asset</param>
        public void AddFont(string AssetPath, string AssetName)
        {
            if (!m_fontLookup.ContainsKey(AssetName))
            {
                _Font tmp = new _Font(AssetPath);

                m_fontLookup.Add(AssetName, m_fontIndex);
                m_fontDB.Add(m_fontIndex, tmp);
                m_fontIndex++;
            }
            else
            {
                _Font tmp = m_fontDB[m_fontLookup[AssetName]];
                tmp.refCount += 1;
                m_fontDB[m_fontLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this asset is no longer used by any objects, it is released from main memory.
        /// </summary>
        /// <param name="AssetName">The asset lookup key</param>
        public void ReleaseFont(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
            {
                _Font tmp = m_fontDB[m_fontLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                    tmp.refCount -= 1;

                m_fontDB[m_fontLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a asset from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the asset name</param>
        public void DeleteFont(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
            {

                m_fontDB.Remove(m_fontLookup[AssetName]);
                m_fontLookup.Remove(AssetName);
            }
        }
        #endregion

        /// <summary>
        /// This removes all of the contents of all asset databases.
        /// </summary>
        public void UnloadContent()
        {
            m_effectDB.Clear();
            m_modelDB.Clear();
            m_textureDB.Clear();
            m_fontDB.Clear();

            m_fontLookup.Clear();
            m_textureLookup.Clear();
            m_modelLookup.Clear();
            m_effectLookup.Clear();

            m_textureIndex = 0;
            m_fontIndex = 0;
            m_modelIndex = 0;
            m_effectIndex = 0;
        }
    }
}




#5188957 When to Recruit

Posted by slayemin on 24 October 2014 - 12:46 PM

If he's talking about getting people to work on the project for free, though, these things become much more complicated.

Yes, I agree. Free labor is cheap, but you get what you pay for. I think volunteer projects are great for learning new things and getting experience, but it's excruciatingly difficult to seriously build a game which you want to release based on free labor. I personally would never attempt it or join a volunteer project. That's not to say it can't work though. I met a team of developers upstairs in our building who are all working together for free on an HTML5 MMO (top down shooter). Each of them are living off of personal savings from prior jobs and have been slaving away on this project for almost two years. One of them has until next summer until his bank account runs dry, so you can guess what kind of stress and pressure they're under. So, I suppose it's worth noting that "free" labor is never free for everyone -- bills and living expenses don't stop.

Game design needs to be done from day 1.

 
Only as little as possible to avoid severe creative differences.
 
Nobody wants to work for free on somebody else's game design.  It's much more useful to motivate people by giving them input in the design itself, so it's not "my game", it's "our game", which is a kind of creative reward that has value in itself even without pay.
 
You have to narrow the game design down enough to avoid a complete lack of direction, feature creep, or conflicts between team members.  Such as:  one only likes JRPGs, the other only likes FPS - how do you resolve this?  You don't.  You only recruit one of them based on their having similar interests in line with the overall goal.
 
Narrow it down only just enough, but leave it open so contributors can feel like it's their game.  Achieving that balance in itself is something of an art.

I disagree and still stand by my original claim.

Imagine you're brought on as an artist onto a project and are told by someone, who doesn't really know what they want, to make the art for their game. You have no idea what they're trying to build and it could change at a moments notice. A detailed game design is something solid to stand on and something to work off of. It tells you exactly what needs to be built and how it should all work together with other parts of the game. Without it, it's like you're standing on a constantly shifting sand dune which changes form with the winds. When you have to build concrete assets, it's excellent to have as much requirements definition as possible. Naturally, the game designer can't detail out every single nuanced detail, so the *creative* part for an artist is in the actual creative production of the asset. The look, feel, character, style, etc.

If you think the artist is going to be annoyed by a lack of definition, just imagine how much more annoyed a programmer would be (speaking as a programmer myself). I WILL not write up a complete game system if I don't know exactly how it needs to work. There isn't any "figure it out as you go". I also happen to design my own game, but I haven't designed all software I've built. There is nothing more sexy to me as a programmer than an air tight design. There's no thinking required, just implement it to the spec! There is no confusion. You know what needs to be built. It's very refreshing to work on projects with clear definition.

One thing you do allude to, which is critical, is "stakeholder buy-in". You don't need to give everyone on the team creative control / input on the game design. Chances are actually really good that most people are actually not very good game designers. It's actually *really hard* to do correctly. A programmer is an expert at writing code. An artist is an expert at art. A producer is supposed to be an expert at project management. A writer is an expert at writing. Game designers are experts at building game systems. You wouldn't want your artist writing code, or your programmer creating art, so why would you have either of them doing game design? That's not their specialty, and not the only way they have creative input into the production of a game.

Art and style guides needs to be done from day 1.

Where volunteer projects are concerned, that's almost impossible.  People will come and go from week to week, maybe only contribute a couple sketches, or one sprite.  Things are unfortunately very unlikely to match.  
 
And most artists who will work for free are both unwilling and unable to follow a strict style guide.  
 
The rare and magical exception to this is FAN games, which are ripping off a very popular IP that the artists want to copy.
For example, some Zelda fan games have gone pretty far in getting various artists to work together to make consistent assets.  They are also more motivated by fandom.
However, this is also illegal.
 
For programming, it's a little easier for somebody to drop in and contribute a function or two.
 
In the case of artists, you may either have to learn to accept inconsistency, or bite the bullet and pay for art.
 
If you're really lucky, you may find a half decent artist who will contribute to the project in a big way for free, but you'll have to pay them in spades in terms of letting them reign as king over game design and story in order to get that kind of investment.  You'd basically just be finding an artist to work for to make his or her game idea.  And even then, they may still flake off one by one, and have to be replaced, completely gutting all of the game's story and art for a new artist to move in.  
 
Keep it simple, and make sure all the art for the game can be finished in a few weeks by one artist; that will maximize your chances of making it work.

Yes, this is bad. What you describe is exactly what you want to avoid in a serious project. Pick any well produced game and really look at the art style in it. It's consistent. The color palettes are intentionally chosen to create an intended feeling / atmosphere. Think of Skyrim, how they have a bit of a saturated color palette and pretty realistic character models and environments. Now, imagine what Skyrim would have looked like if they had 30+ artists come in, each with their very own unique interpretations on what the game should look like. Some would go for a my stylized approach, others go for hyper-realistic, some go for cartoonish, etc. In a vacuum, each art asset would look good, but when you put them all into the same universe together, you can't convince the player that the game world has consistency and you break the suspension of disbelief, which causes a loss of player immersion (which some mods break).

As for programming, it's equally problematic to have people pop in and write up a function or two and disappear. As a programmer, you *have* to be familiar with the code base / framework you're working within. That costs time for us to get familiar with it (ramp up time). If a programmer doesn't spend time getting familiar with what they're working with, they run a huge risk of breaking something or writing code which has already been done.

Anyways, I guess my whole point here is that people are super important to the success of a game project. In some jobs, people can be treated like interchangeable components -- not in game development! The ramp-up time for a person to really get into a project and become an expert is long, and you can't ever carelessly throw away your most valuable resource (staff) through people mismanagement. If you're going to be serious about building a game and releasing it commercially, you can't afford to screw up the building of your team. If you're just working on a school project or hobby project, recruit away!


#5188799 When to Recruit

Posted by slayemin on 23 October 2014 - 01:31 PM

Depends on what you're recruiting for and what the work load is. You'll want to recruit different skill sets at different phases of the project.

 

Game design needs to be done from day 1.

Art and style guides needs to be done from day 1.

Programming can be done when 50% of the design has been figured out, though there's no harm in starting early.

Business planning needs to be done before the project even starts.

Marketing can be done when the project is about 90% complete (keep in mind, the last 10% takes the longest)

Writers can be brought in roughly around 50%-60% production phase (my guess).
Testing needs to be done when you've got something playable, but the testing work load ramps up as the project nears completion. You can probably get away with not having dedicated testers for the first half of a project. Keep in mind though, issues not found through testing snowball in the sheer amount of effort required to fix them. Finding and fixing problems early can save you TONS of time and money -- fixing hypothetical bug A at day of introduction + 2 costs a programmer 2 hours of work, which translates to $40 * 2 = $80. Fixing bug A at day of introduction + 200 costs a programmer 30 hours of work (due to refactoring all dependent systems), which translates to $40 * 30 = $1200.

When do you need to hire someone? When you have too much work built up for one person to handle or when you can't do the work yourself.

IF you have a lot of work, something someone can spend 40 hours/week doing, then you want to hire an employee.

IF you have a bit of piecemeal work, consider contracting it out instead.

Quick note on this: Contractors are generally more expensive per hour than employees, but will save you money if you don't have a lot of consistent work. You don't want to pay employees to sit around doing nothing. Employees on the other hand, give you consistency and continuity which you don't really get with contractors / freelancers. You generally don't want to contract out stuff which has a lot of stylistic variety, such as artwork.

IF you don't want to pay someone to work for you, be prepared to have them quit on you at any moment. Unpaid people have very little incentive other than their own morale / personal interests to stick around. If your making an unpaid person fill a vital role in your project, you're taking on a huge risk in terms of project management.

IF you want to build a geographically remote team of unpaid people, good luck. Not only may members drop off the project at any moment, you will also have huge communication challenges. You've got people all over the world with different time zones, languages, schedules, and cultural backgrounds. Project management and coordination of effort will be a nightmare. You're essentially operating a free open source project, where you should expect people to pop in and tinker a bit and disappear. Don't expect a lot of progress, or for the progress to go in the direction you want, to your level of satisfaction.




#5188393 Starting Slow

Posted by slayemin on 21 October 2014 - 02:52 PM

Start with Tic-tac-toe. It's pretty much the game version of "hello world". It'll get you familiar with building for a mobile platform and you won't get stuck in some game design / programming problem BS. If you can't do tic-tac-toe on the phone, you need to increase your mobile dev technical chops -- building anything more complicated than that is just a recipe for failure and disappointment. Once you've got that handled and know how the mobile development workflow works, you can build more complicated projects.




#5187257 what is the correct way to move object along direction

Posted by slayemin on 15 October 2014 - 04:46 PM

You can also get a direction vector from an angle:

//theta is a float value between 0->2pi
dirX = speed * cos(theta);
dirY = speed * sin(theta);

So, movement in a direction is pretty simple:

Position.X += dirX;
Position.Y += dirY;

Then you can wire up some input controls to change the direction and speed.




#5186218 Your thoughts on me hiring a game developer/studio

Posted by slayemin on 10 October 2014 - 12:16 PM

I avoided the word "hire" because he'll need to feel like this is as much his project he's interested in doing.



So you're not going to pay someone to work for you?!

No, no no. I get what you're trying to say, but this is a really bad move. If you go through the effort and trouble to find that perfect fit for your project and team, you want to lock that person down so that they stay on the project/team! The best way to do this is to take care of them by satisfying their interests, which include getting paid well, regularly, and on time. This is business 101. You need this programmer to be a stakeholder in your project, and if you aren't a stakeholder invested in them, they aren't going to be a stakeholder invested in you. They'll jump ship as soon as something else comes along which satisfies their interests / desires more than you do. This would be a catastrophe and spell doom for your project since its such a large setback. It's hard to bounce back from. So, don't make the foolish mistake of letting it happen in the first place. Hire people.
 

My ideas are just ideas for him to see whether he wants to be interested in doing it and pretty much making it his own, with the input of my ideas as well

I'm a programmer and design my own games. This is a huge red flag. I underestimated the difficulty of good game design, and understand it takes a lot of effort to do it right. If *you* are going to be the game designer, then you need to be beyond stellar and work harder than anyone else on the team, and spell out in precise, exact detail how every minute aspect of the game is going to work together. The fact that you want to do some vague hand waving and essentially tell the programmer to fill in the gaps with their own ideas, screams the fact that you don't know exactly what you want. There's nothing more frustrating than to spend a ton of effort writing code to implement something which ends up changing completely or getting scrapped on the whims of an uncertain and constantly shifting design. If you're expecting the programmer to take control on the direction of this project, you reduce yourself to a niggling background voice which needs to be appeased while the programmer works on his own ideas of what should be built. Warning: Programmers aren't necessarily good designers!!!
 

I tell them to make a site like this site here, and if they cannot, then I regard them as failing to meet client's expectations.


This is not necessarily the right conclusion to make. Just because a developer fails to "build a site" doesn't mean that the developer can't do the job and is thus incompetent. No project exists in a vacuum. The project can be doomed before it even begins due to a failure in any number of things which lead up to the project and its management. I strongly recommend that you familiarize yourself with the software development life cycle. Whether you're building websites, video games, or applications, it's all the same process.
Just to refresh your memory on the lead up to constructing software, you have the following phases:
-1. Visioning step - What would be awesome to do?
0. Feasibility assessment - Can the vision actually be done within the limitations?
1. Requirements gathering - What *must* the software do? Does it meet the vision? Is it feasible?
2. Design - How will the software work to satisfy the requirements? Is it feasible? does it satisfy the vision?
3. Construction - build the damned thing! write the code! create the assets! make it come to life! Does it meet the design? Does it meet the requirements? Can it even work with the technical limitations / constraints?

Notice how each phase of the process depends on the phases preceding it? If any of the steps leading up to the construction phase are shit, it doesn't matter how good the programmer may be, the project is going to fail. The only way a shit project can be saved is if the programmer realizes he got set up for failure and goes through and redoes the work proceeding the work he's supposed to be doing. The right move to do when this happens is to just abandon the project/client and move onto working for/with someone who actually knows what the hell they're doing (some more unscrupulous devs will just string the client along and suck their money dry to get a steady paycheck). Sure, the shit client will bad mouth the programmer for being incompetent/incapable, but what does the programmer care? They've got better people to work for than to concern themselves with what bad clients have to say about them.
 

Yea, maybe that could be more my role, as I'm already an investor for two other companies. I feel maybe being an investor for this one also could be a possibility. But for this one, I have that itch of wanting to have that perfect game I've always wanted to play be realized. Alot of games out there may have 90% of what I'm looking for, and they are all very great games, just that I have that itch to make it 100% so if I can't find it out there already, then only other option is to try to make it. Investing, I'm always going to be doing even after I die.

I'm also an investor (95% stocks). I have also invested in my own indie game studio and put my money where my mouth is. My investment is more than just throwing money at a project/company and hoping for success, it's also a very deeply personal investment of my own time, commitment, and 100% energies. I'm totally invested in myself, and it's a do-or-die situation. With every investment, someone somewhere down the money stream needs to stand up and make use of the financial resources they have been granted to further the goal of the project/organization. The worst way to 'invest' in this sense is to throw money at a new studio/project with a half-assed game design and non-commitaly hiring some shammy programmer to make it happen -- you need to be the leader in the trenches, bringing your troops to victory by being the point man leading the charge. If you aren't/can't, your ROI will be -100%. Even if you have $0 financially invested, you can still be heavily invested in the success of a project. If you aren't 100% invested in a project/company in every way, you can't be the leader who asks others to invest 100%.




#5184142 Geometry shader-generated camera-aligned particles seemingly lacking Z writing

Posted by slayemin on 30 September 2014 - 03:09 PM

I had this problem last week and I was struggling to figure out what exactly was going on. Then I found this super awesome article by Shawn Hargreaves:

http://blogs.msdn.com/b/shawnhar/archive/2009/02/18/depth-sorting-alpha-blended-objects.aspx

 

He pretty much explains in perfect detail what the problem is and provides some solutions. IF you are using alpha blending, the "best" solution is to sort your objects based on their distance from the camera and draw them from back to front.

 

I too decided to write my billboards into HLSL code. Unfortunately since I'm using XNA, I only have the vertex shader and pixel shader at my disposal, so I can't add extra verticies as you would be able to do in DX10+. This means that I just have to include the vertex positions in the quad which gets rendered instead of inferring the vertices from a point positition. In my implementation, I take advantage of instancing which allows me to send a huge batch of vertex instance data to the GPU once, and then I let the shader process the primitive data and draw updates, using just one draw call. 

 

Here is the HLSL code I'm using for DX9:
 

float4x4 World;
float4x4 View;
float4x4 Projection;
float3 CameraPosition;
float3 CameraUp;
float CurrentTime;			//current time in seconds
float4 Tint : COLOR0 = (float4)1;
bool UseWorldTransforms;
float4 Ambient : COLOR = (float4)1;			//the ambient light color in the scene
float3 LightDirection;			//a vector3 for the directional light
float4 LightColor : COLOR;		//the directional light color

const float Tau = 6.283185307179586476925286766559;

//------- Texture Samplers --------
Texture g_Texture;
sampler TextureSampler = sampler_state { texture = <g_Texture>; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = wrap; AddressV = wrap;}; 

struct QuadTemplate
{
	float3 Position	    : POSITION0;
	float2 TexCoord	    : TEXCOORD0;
};

struct QuadInstance
{
	float3 Position		: POSITION1;
	float3 Velocity		: POSITION2;
	float3 Normal		: NORMAL0;
	float3 Up			: NORMAL1;
	float3 ScaleRot		: POSITION3;
	float3 EndScaleRot	: POSITION4;
	float2 Time			: POSITION5;
	float4 Color		: COLOR0;
	float4 EndColor		: COLOR1;
};

struct LineSegmentInput
{
	float3 Position		: POSITION0;
	float4 StartColor	: COLOR0;
	float4 EndColor		: COLOR1;
	float3 Velocity		: TEXCOORD0;
	float2 Time			: TEXCOORD1;
	float2 UV		    : TEXCOORD2;
};

struct VSOUT
{
    float4 Position		: POSITION0;			//screen space coordinate of pixel
	float4 Color		: COLOR;				//vertex color
	float2 TexCoord		: TEXCOORD0;			//texture coordinate
};

float3x3 CreateRotation(float myAngle, float3 rotAxis)
{
	float c = cos(myAngle);
	float s = sin(myAngle);
	float3 u = rotAxis;

	return float3x3(
	c + (u.x*u.x)*(1-c), u.x*u.y*(1-c) - u.z*s, u.x*u.z * (1-c) + u.y*s,
	u.y*u.x*(1-c) + u.z * s, c + u.y*u.y*(1-c), u.y*u.z*(1-c) - u.x*s,
	u.z*u.x*(1-c) - u.y * s, u.z*u.y*(1-c)+u.x*s, c + u.z*u.z*(1-c)
	);
}

//3D TEXTURED//////////////////////////////////////////////////////////////////////////////////
VSOUT VS_3DTex(QuadTemplate input)
{
    VSOUT output = (VSOUT)0;

    //float4 worldPosition = mul(input.Position, World);
    //float4 viewPosition = mul(worldPosition, View);
    //output.Position = mul(viewPosition, Projection);
	output.Position = (float4)0;
	output.Color = (float4)1;
	//output.Color.r = input.TextureCoord.x;
	//output.Color.g = input.TextureCoord.y;
	//output.TexCoord = input.TextureCoord;
	output.TexCoord = (float2)0;

    return output;
}

//3D Textured thick lines/////////////////////////////////////////////////////////////////
VSOUT VS_3DTexturedLine(LineSegmentInput input)
{
	VSOUT output = (VSOUT)0;

	float age = CurrentTime - input.Time.x;
	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(input.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float4 pos = (float4)1;
	pos.xyz = input.Position + (input.Velocity * age);
	if(UseWorldTransforms == false)
		pos.xyz += CameraPosition;

	output.Position = mul(mul(pos, View), Projection);
	output.Color = lerp(input.StartColor, input.EndColor, lifeAmt);
	output.TexCoord = input.UV;

	return output;
}

//3D colored 0px lines////////////////////////////////////////////////////////////////////
VSOUT VS_3DLineSegment(LineSegmentInput input)
{
	VSOUT output = (VSOUT)0;

	float age = CurrentTime - input.Time.x;
	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(input.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float4 pos = (float4)1;
	pos.xyz = input.Position + (input.Velocity * age);
	if(UseWorldTransforms == false)
		pos.xyz += CameraPosition;

	output.Position = mul(mul(pos, View), Projection);
	output.Color = lerp(input.StartColor, input.EndColor, lifeAmt);

	return output;
}

//3D Textured Quads///////////////////////////////////////////////////////////////////////
VSOUT VS_3DQuadTex(QuadTemplate input, QuadInstance instance)
{

	float age = CurrentTime - instance.Time.x;

	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(instance.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float3 m_scale = lerp(instance.ScaleRot, instance.EndScaleRot, lifeAmt);		//linear interpolate the scale values to get current scale
	float m_rotation = instance.ScaleRot.z + (instance.EndScaleRot.z * age);	//current rotation is initial rotation + sum of rotational speed over time
	float3 m_center = instance.Position;			//this is the transformed center position for the quad.
	
	m_center +=  (instance.Velocity * age);

	//TODO: Handle the case where the normal is set to (0,1,0) or (0,-1,0)
	//Note: this is done in the application, not the shader.

	float3 m_normal = instance.Normal;										//the normal is going to be given to us and is fixed.
	//float3 m_up = float3(0,1,0);											//the up vector is simply a cross of the left vector and normal vector
	float3 m_up = instance.Up;
	float3 m_left = cross(m_normal, m_up);									//the left vector can be derived from the camera orientation and quad normal
	m_up = cross(m_left, m_normal);

	float3x3 m_rot = CreateRotation(-m_rotation, m_normal);					//Create a rotation matrix around the object space normal axis by the given radian amount.
																			//This rotation matrix must then be applied to the left and up vectors.
	m_left = mul(m_left, m_rot) * m_scale.x;								//apply rotation and scale to the left vector
	m_up = mul(m_up, m_rot) * m_scale.y;									//apply rotation and scale to the up vector

	//Since we have to orient our quad to always face the camera, we have to change the input position values based on the left and up vectors.
	//the left and up vectors are in untranslated space. We know the translation, so we just set the vertex position to be the translation added to
	//the rotated and scaled left/up vectors.

	float3 pos = (float)0;
	if(input.Position.x == -1 && input.Position.y == -1)			//bottom left corner
	{
		pos = m_center + (m_left - m_up);	
	}
	else if(input.Position.x == -1 && input.Position.y == 1)		//top left corner
	{
		pos = m_center + (m_left + m_up);
	}
	else if(input.Position.x == 1 && input.Position.y == 1)			//top right corner
	{
		pos = m_center - (m_left - m_up);
	}
	else															//bottom right corner
	{
		pos = m_center - (m_left + m_up);
	}

	//Since we've already manually applied our world transformations, we can skip that matrix multiplication.
	//note that we HAVE to use a Vector4 for the world position because our view & projection matrices are 4x4.
	//the matrix multiplication function isn't smart enough to use a vector3. The "w" value must be 1.

	float4 worldPosition = 1.0f;
	worldPosition.xyz = pos;
	if(UseWorldTransforms == false)
		worldPosition.xyz += CameraPosition;

	VSOUT output;
    output.Position = mul(mul(worldPosition, View), Projection);
	output.Color = lerp(instance.Color, instance.EndColor, lifeAmt);
	output.TexCoord = input.TexCoord;
	return output;
}

//3D Textured point sprites///////////////////////////////////////////////////////////////////////
VSOUT VS_3DPointSpriteTex(QuadTemplate input, QuadInstance instance)
{
	/* 
	SUMMARY: A point sprite is a special type of quad which will always face the camera. The point sprite
	can be scaled and rotated around the camera-sprite axis (normal) by any arbitrary angle. Because of these
	special behaviors, we have to apply some special instructions beyond just multiplying a point by the world
	matrix.
	*/

	float age = CurrentTime - instance.Time.x;

	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(instance.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float3 m_scale = lerp(instance.ScaleRot, instance.EndScaleRot, lifeAmt);		//linear interpolate the scale values to get current scale
	float m_rotation = (instance.ScaleRot.z * Tau) + (instance.EndScaleRot.z * Tau * age);	//current rotation is initial rotation + sum of rotational speed over time
	float3 m_center = instance.Position;			//this is the transformed center position for the quad.
	m_center +=  (instance.Velocity * age);

	float3 m_normal = normalize(CameraPosition - m_center);					//the normal is going to be dependent on the camera position and the center position
	float3 m_left = cross(m_normal, CameraUp);								//the left vector can be derived from the camera orientation and quad normal
	float3 m_up = cross(m_left, m_normal);									//the up vector is simply a cross of the left vector and normal vector
	float3x3 m_rot = CreateRotation(m_rotation, m_normal);					//Create a rotation matrix around the object space normal axis by the given radian amount.
																			//This rotation matrix must then be applied to the left and up vectors.
	m_left = mul(m_left, m_rot) * m_scale.x;								//apply rotation and scale to the left vector
	m_up = mul(m_up, m_rot) * m_scale.y;									//apply rotation and scale to the up vector

	//Since we have to orient our quad to always face the camera, we have to change the input position values based on the left and up vectors.
	//the left and up vectors are in untranslated space. We know the translation, so we just set the vertex position to be the translation added to
	//the rotated and scaled left/up vectors.

	float3 pos = (float)0;
	if(input.Position.x == -1 && input.Position.y == -1)			//bottom left corner
	{
		pos = m_center + (m_left - m_up);	
	}
	else if(input.Position.x == -1 && input.Position.y == 1)		//top left corner
	{
		pos = m_center + (m_left + m_up);
	}
	else if(input.Position.x == 1 && input.Position.y == 1)			//top right corner
	{
		pos = m_center - (m_left - m_up);
	}
	else															//bottom right corner
	{
		pos = m_center - (m_left + m_up);
	}

	//Since we've already manually applied our world transformations, we can skip that matrix multiplication.
	//note that we HAVE to use a Vector4 for the world position because our view & projection matrices are 4x4.
	//the matrix multiplication function isn't smart enough to use a vector3. The "w" value must be 1.

	float4 worldPosition = 1.0f;
	worldPosition.xyz = pos;
	if(UseWorldTransforms == false)
		worldPosition.xyz += CameraPosition;

	VSOUT output;
    output.Position = mul(mul(worldPosition, View), Projection);
	output.Color = lerp(instance.Color, instance.EndColor, lifeAmt);
	output.TexCoord = input.TexCoord;
	return output;
}

//3D Textured Billboard///////////////////////////////////////////////////////////////////////
VSOUT VS_3DBillboardTex(QuadTemplate input, QuadInstance instance)
{
	/* 
	SUMMARY: A billboard is a special type of quad which will always face the camera, but is constrained along the
	y-axis. The billboard can be scaled and rotated around the camera-sprite axis (normal) by any arbitrary angle. 
	Because of these special behaviors, we have to apply some special instructions beyond just multiplying a point 
	by the world matrix.
	*/

	float age = CurrentTime - instance.Time.x;								//total elapsed time since birth

	float lifeAmt = 0;															//the age is a percentage between birth and death, if we're not -1
	if(instance.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float3 m_scale = lerp(instance.ScaleRot, instance.EndScaleRot, lifeAmt);		//linear interpolate the scale values to get current scale
	float m_rotation = (instance.ScaleRot.z * Tau) + (instance.EndScaleRot.z * Tau * age);	//current rotation is initial rotation + sum of rotational speed over time
	float3 m_center = instance.Position;			//this is the transformed center position for the quad.
	m_center +=  (instance.Velocity * age);

	float3 m_normal = CameraPosition - m_center;							//the normal is going to be dependent on the camera position and the center position
	m_normal.y = 0;
	m_normal = normalize(m_normal);
	float3 m_up = float3(0,1,0);											//the up vector is simply the unit Y value
	float3 m_left = cross(m_normal, m_up);								//the left vector can be derived from the camera orientation and quad normal
	
	float3x3 m_rot = CreateRotation(m_rotation, m_normal);					//Create a rotation matrix around the object space normal axis by the given radian amount.
																			//This rotation matrix must then be applied to the left and up vectors.
	m_left = mul(m_left, m_rot) * m_scale.x;								//apply rotation and scale to the left vector
	m_up = mul(m_up, m_rot) * m_scale.y;									//apply rotation and scale to the up vector

	//Since we have to orient our quad to always face the camera, we have to change the input position values based on the left and up vectors.
	//the left and up vectors are in untranslated space. We know the translation, so we just set the vertex position to be the translation added to
	//the rotated and scaled left/up vectors.

	float3 pos = (float)0;
	if(input.Position.x == -1 && input.Position.y == -1)			//bottom left corner
	{
		pos = m_center + (m_left - m_up);	
	}
	else if(input.Position.x == -1 && input.Position.y == 1)		//top left corner
	{
		pos = m_center + (m_left + m_up);
	}
	else if(input.Position.x == 1 && input.Position.y == 1)			//top right corner
	{
		pos = m_center - (m_left - m_up);
	}
	else															//bottom right corner
	{
		pos = m_center - (m_left + m_up);
	}

	//Since we've already manually applied our world transformations, we can skip that matrix multiplication.
	//note that we HAVE to use a Vector4 for the world position because our view & projection matrices are 4x4.
	//the matrix multiplication function isn't smart enough to use a vector3. The "w" value must be 1.

	float4 worldPosition = 1.0f;
	worldPosition.xyz = pos;
	if(UseWorldTransforms == false)
		worldPosition.xyz += CameraPosition;

	VSOUT output;
    output.Position = mul(mul(worldPosition, View), Projection);
	output.Color = lerp(instance.Color, instance.EndColor, lifeAmt);
	output.TexCoord = input.TexCoord;
	return output;
}

//3D Vertex colors only///////////////////////////////////////////////////////////////////////////
VSOUT VS_3D(float4 inPosition : POSITION, float4 inColor : COLOR)
{
    VSOUT output;

    float4 worldPosition = mul(inPosition, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
	output.Color = inColor;
	output.TexCoord = 0;

    return output;
}

VSOUT VS_2D(float4 inPos : POSITION, float4 inColor : COLOR)
{
    VSOUT Output = (VSOUT)0;

	Output.Position = inPos;
	Output.Color = inColor;

    return Output;
}
//PIXEL SHADERS///////////////////////////////////////////////////////////////////////////

float4 PS_3D(VSOUT output) : COLOR0
{
	//Output.Color = tex2D(TextureSampler, vs_output.TextureCoord);
	//Output.Color.rgb *= saturate(PSIn.LightingFactor) + xAmbient;

	//return tex2D(TextureSampler, output.TexCoord);

	return output.Color * Tint * Ambient;
}

float4 PS_2D(VSOUT vs_output) : COLOR0
{
	return vs_output.Color * Tint;
}

float4 PS_3DTex(VSOUT input) : COLOR0
{
	float4 c = tex2D(TextureSampler, input.TexCoord);
	if(c.a <= 0.1)
		discard;
	//return c * (input.Color + Ambient);
	float3 Up = (float3)0;
	Up.y = 1;

	float LightFactor = dot(Up, -LightDirection);

	return  (c * input.Color * Tint) * saturate(LightColor + Ambient);
	//return  ((c * input.Color ) * saturate((LightColor * LightFactor) + Ambient)) ;
}

//TECHNIQUES///////////////////////////////////////////////////////////////////////////
technique Technique2D
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS_2D();
        PixelShader = compile ps_2_0 PS_2D();
    }
}

technique VertexColor3D
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS_3D();
        PixelShader = compile ps_2_0 PS_3D();
    }
}

technique Textured3D
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS_3DTex();
        PixelShader = compile ps_2_0 PS_3DTex();
    }
}

technique TexturedQuad3D
{
	pass Pass0
	{
	    VertexShader = compile vs_3_0 VS_3DQuadTex();
            PixelShader = compile ps_3_0 PS_3DTex();
	}
}

technique TexturedPointSprite3D
{
	pass Pass0
	{
	    VertexShader = compile vs_3_0 VS_3DPointSpriteTex();
            PixelShader = compile ps_3_0 PS_3DTex();
	}
}

technique TexturedBillboard3D
{
	pass Pass0
	{
	   VertexShader = compile vs_3_0 VS_3DBillboardTex();
           PixelShader = compile ps_3_0 PS_3DTex();
	}
}

technique LineSegment3D
{
	pass Pass0
	{
	    VertexShader = compile vs_3_0 VS_3DLineSegment();
            PixelShader = compile ps_3_0 PS_3D();
	}
}

technique TexturedLine3D
{
	pass Pass0
	{
		VertexShader = compile vs_3_0 VS_3DTexturedLine();
        PixelShader = compile ps_3_0 PS_3DTex();
	}
}

And here is my complete rendering code for drawing quads, billboards, and point sprites using the above HLSL:
 

public void Render(Camera3D camera, coreTime worldTime)
{
	
	//...snipped irrelevant code...

	if (m_settings.PainterSort)
	{
		PainterSort(camera.Position);
	}

	//rebuild the vertex and index buffers if the collection has been changed.
	if (m_dirtyBuffers > 0)
		RebuildBuffers();

	//activate our buffers
	//m_settings.GraphicsDevice.SetVertexBuffer(m_psVB);
	RasterizerState rs = m_settings.GraphicsDevice.RasterizerState;
	if (m_settings.DoubleSided == true)
	{
		RasterizerState rs2 = new RasterizerState();
		rs2.CullMode = CullMode.None;
		m_settings.GraphicsDevice.RasterizerState = rs2;
	}

	m_settings.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
	m_settings.GraphicsDevice.Indices = m_IB;
	m_settings.GraphicsDevice.BlendState = m_settings.BlendState;

	m_effect.Parameters["g_Texture"].SetValue(m_settings.Texture);
	m_effect.Parameters["UseWorldTransforms"].SetValue(m_settings.UseWorldTransforms);
	m_effect.Parameters["View"].SetValue(camera.View);
	m_effect.Parameters["Projection"].SetValue(camera.Projection);
	m_effect.Parameters["CameraPosition"].SetValue(camera.Position);
	m_effect.Parameters["CameraUp"].SetValue(camera.Up);
	m_effect.Parameters["CurrentTime"].SetValue((float)worldTime.TotalWorldTime.TotalSeconds);
	m_effect.Parameters["Tint"].SetValue(m_settings.Tinting.ToVector4());

	if (m_settings.UseWorldLights)
	{
		m_effect.Parameters["Ambient"].SetValue(BaseSettings.AmbientLight.ToVector4());
		m_effect.Parameters["LightColor"].SetValue(BaseSettings.AllDirLights[0].Color.ToVector4());
		m_effect.Parameters["LightDirection"].SetValue(BaseSettings.AllDirLights[0].Direction);
	}
	

	#region Draw Quads
	if (m_quadVB != null && m_quadVB.VertexCount > 0)
	{
		m_effect.CurrentTechnique = m_effect.Techniques["TexturedQuad3D"];

		m_settings.GraphicsDevice.SetVertexBuffers(
		new VertexBufferBinding(m_VB, 0, 0),
		new VertexBufferBinding(m_quadVB, 0, 1));

		foreach (EffectPass pass in m_effect.CurrentTechnique.Passes)
		{
			pass.Apply();
			//m_settings.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_psList.Count * 4, 0, m_psList.Count * 2);
			m_settings.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 
				0, //base vertex
				0,  //min vertex index
				4, //vertex count
				0, //start index
				2, //primitive count
				m_quadVB.VertexCount  //instance count
				);
		}
	}
	#endregion

	#region Draw Point sprites
	if (m_psVB != null && m_psVB.VertexCount > 0)
	{
		m_effect.CurrentTechnique = m_effect.Techniques["TexturedPointSprite3D"];

		m_settings.GraphicsDevice.SetVertexBuffers(
		new VertexBufferBinding(m_VB, 0, 0),
		new VertexBufferBinding(m_psVB, 0, 1));

		foreach (EffectPass pass in m_effect.CurrentTechnique.Passes)
		{
			pass.Apply();
			//m_settings.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_psList.Count * 4, 0, m_psList.Count * 2);
			m_settings.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0,
				4, //vertex count
				0, //start index
				2, //primitive count
				m_psVB.VertexCount  //instance count
				);
		}
	}
	#endregion

	#region Draw billboards
	if (m_bbVB != null && m_bbVB.VertexCount > 0)
	{
		m_effect.CurrentTechnique = m_effect.Techniques["TexturedBillboard3D"];

		m_settings.GraphicsDevice.SetVertexBuffers(
		new VertexBufferBinding(m_VB, 0, 0),
		new VertexBufferBinding(m_bbVB, 0, 1));

		foreach (EffectPass pass in m_effect.CurrentTechnique.Passes)
		{
			pass.Apply();
			//m_settings.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_psList.Count * 4, 0, m_psList.Count * 2);
			m_settings.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0,
				4, //vertex count
				0, //start index
				2, //primitive count
				m_bbVB.VertexCount  //instance count
				);

		}
	}
	#endregion

	m_settings.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
	m_settings.GraphicsDevice.RasterizerState = rs;
	m_settings.GraphicsDevice.BlendState = BlendState.Opaque;
}

Here are both custom vertex definitions I came up with which are necessary for drawing the instanced primitives using HLSL:
QuadInstanceVertex:
 

public struct QuadInstanceVertex
{

	//Optimize only if you have been able to profile a problem with the vertex byte size. Don't prematurely optimize and over-engineer.

	/// <summary>
	/// Offset from origin
	/// </summary>
	public Vector3 Position;

	/// <summary>
	/// Particle velocity
	/// </summary>
	public Vector3 Velocity;

	/// <summary>
	/// Normal direction for the quad face
	/// Quad: Set value; PointSprite: (0,0,0); BillBoard: (0,0,0)
	/// </summary>
	public Vector3 Normal;

	/// <summary>
	/// We actually have to include the up vector for quads because we just can't derive it within the shader.
	/// </summary>
	/// <remarks>
	/// The problem with trying to derive an up direction within the shader is that the shader compiler will UNROLL
	/// all of your branching logic. If you try to write any code to avoid dividing by zero, one of the branches will
	/// take that path anyways and divide by zero, causing *visual studio* to crash. So, rather than trying to run
	/// logic in the shader, we have to run it in the application.
	/// </remarks>
	public Vector3 Up;

	/// <summary>
	/// The starting width, length, and normal-axis rotation
	/// </summary>
	public Vector3 Scale;

	/// <summary>
	/// the end width, length and normal-axis rotational speed.
	/// length and width with be lerp'd, rotational speed will be added to initial value
	/// </summary>
	public Vector3 EndScale;

	/// <summary>
	/// Crucial timing values for the vertex shader.
	/// X = Spawn time of the particle/quad
	/// Y = lifespan of the quad; 
	///       -1: always alive
	///        0: dead
	///       0+: alive
	/// </summary>
	/// <remarks>Your quad manager is responsible for removing a quad/particle when the lifespan reaches zero.</remarks>
	public Vector2 Time;

	/// <summary>
	/// The starting color.
	/// </summary>
	public Color Color;

	/// <summary>
	/// The ending color. Current value will be lerp'd between this and start color.
	/// </summary>
	public Color EndColor;


	
	/// <summary>
	/// Creates a particle with the following properties.
	/// </summary>
	/// <param name="pos">The position offset from the origin</param>
	/// <param name="norm">the normal of the face</param>
	/// <param name="scaleRot">initial length and width</param>
	/// <param name="color">initial color</param>
	/// <param name="zrot">initial rotation around the z-axis</param>
	/// <param name="endScaleRot">end length and width</param>
	/// <param name="t">x = spawn time; y = lifespan (-1: infinite, 0: dead; gt 0: alive)</param>
	/// <param name="dz">rotational speed</param>
	/// <param name="vel">velocity of the particle (if you want it to move)</param>
	/// <param name="endColor">end color of the particle</param>
	public QuadInstanceVertex(Vector3 pos, Vector3 vel, Vector3 norm, Vector3 up, Vector3 scaleRot, Vector3 endScaleRot, Color color, Color endColor, Vector2 t)
	{
		Position = pos;
		Velocity = vel;
		Normal = norm;
		Up = up;

		Scale = scaleRot;
		EndScale = endScaleRot;

		Color = color;
		EndColor = endColor;
		
		Time = t;
	}

	/// <summary>
	/// Creates a quad instance with the given properties.
	/// </summary>
	/// <param name="pos"></param>
	/// <param name="norm"></param>
	/// <param name="scaleRot"></param>
	/// <param name="color"></param>
	/// <param name="zrot"></param>
	public QuadInstanceVertex(Vector3 pos, Vector3 norm, Vector3 up, Vector3 scaleRot, Color color)
	{
		Position = pos;
		Velocity = Vector3.Zero;
		Normal = norm;
		Up = up;

		Scale = scaleRot;
		EndScale = scaleRot;

		Color = color;
		EndColor = color;
		
		Time = new Vector2(0,-1);
	}

	/*Note: The semantic usage index must be unique across ALL vertex buffers. The geometry vertex buffer already uses Position0 and TexCoord0.*/

	public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
		new VertexElement( 0, VertexElementFormat.Vector3, VertexElementUsage.Position, 1),          //12 (pos)
		new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Position, 2),         //12 (velocity)
		new VertexElement(24, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),           //12 (norm)
		new VertexElement(36, VertexElementFormat.Vector3, VertexElementUsage.Normal, 1),           //12 (up)
		new VertexElement(48, VertexElementFormat.Vector3, VertexElementUsage.Position, 3),         //12 (scale/rot)
		new VertexElement(60, VertexElementFormat.Vector3, VertexElementUsage.Position, 4),         //12 (end scale/rot)
		new VertexElement(72, VertexElementFormat.Vector2, VertexElementUsage.Position, 5),         //8  (time)
		new VertexElement(80, VertexElementFormat.Color, VertexElementUsage.Color, 0),              //4  (color)
		new VertexElement(84, VertexElementFormat.Color, VertexElementUsage.Color, 1)               //4  (end color)
		); 

	public const int SizeInBytes = 76;
}

QuadVertex:

/// <summary>
/// A vertex structure with Position and Texture coordinate data
/// </summary>
public struct QuadVertex : IVertexType
{
	/*So, we're gonna get funky here. The R,G,B components of the color denote any color TINT for the quad. Since we also have an alpha channel, we're going
	 to store the CornerID of the vertex within it!*/
	public Vector3 Position;
	public Vector2 UV;


	/// <summary>
	/// Creates a vertex which contains position, normal, color, and texture UV info
	/// </summary>
	/// <param name="position">The position of the vertex, relative to the center</param>
	/// <param name="uv">The UV texture coordinates</param>
	/// <param name="rotation">A radian value indicating rotation around the normal axis</param>
	public QuadVertex(Vector3 position, Vector2 uv)
	{
		Position = position;
		UV = uv;
	}

	public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
		new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),                              //12 bytes
		new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)                    //8 bytes
		);

	public const int SizeInBytes = 20;

	VertexDeclaration IVertexType.VertexDeclaration
	{
		get { return VertexDeclaration; }
	}
}

Quad Class:

public class Quad
{
	static QuadVertex[] m_verts;
	static int[] m_indices;

	public int Key = -1;

	//This is a vertex which contains all of our instance info. You can use it as either a data container
	//or as a vertex to be used by a vertex shader.
	public QuadInstanceVertex Info;

	private void Init(Vector3 position, Vector3 velocity, Vector3 normal, Vector3 up, Vector3 startSize, Vector3 endSize, Color startColor, Color endColor, Vector2 time)
	{

		BuildVerts();
		BuildIndices();

		Info = new QuadInstanceVertex(position, velocity, normal, up, startSize, endSize, startColor, endColor, time);
	}

	public Quad()
	{
		BuildVerts();
		BuildIndices();
	}

	/// <summary>
	/// Creates a quad based on the given values
	/// </summary>
	/// <param name="position">The center position of the quad</param>
	/// <param name="normal">The normal direction indicates the facing direction for the quad</param>
	/// <param name="orientation">This is the orientation of the quad around the normal axis</param>
	/// <param name="size">This is the scaled size of the quad</param>
	public Quad(Vector3 position, Vector3 normal, Vector3 up, float size, float orientation = 0)
	{
		Init(position, Vector3.Zero, normal, up, new Vector3(size,size, orientation), new Vector3(size,size, orientation), Color.White, Color.White, new Vector2(0,-1));
	}

	/// <summary>
	/// Creates a POINT SPRITE at the given location. Use HLSL code for the rest.
	/// </summary>
	/// <param name="center">the center position of the point sprite</param>
	/// <param name="size">the size of the point sprite</param>
	/// <param name="orientation">the rotation around the normal axis for the point sprite</param>
	public Quad(Vector3 position, float size, float orientation = 0)
	{
		Init(position, Vector3.Zero, Vector3.Zero, Vector3.Up, new Vector3(size, size, orientation), new Vector3(size, size, orientation), Color.White, Color.White, new Vector2(0, -1));
	}

	/// <summary>
	/// Creates a generalized quad for use with hardware instancing.
	/// </summary>
	/// <param name="position">This is the position in the game world</param>
	/// <param name="velocity">This is how much the quad moves each frame</param>
	/// <param name="normal">QUAD Only: This is the facing direction of the quad. Point sprites and billboards will derive this value based on camera position.</param>
	/// <param name="startSize">Starting scale and rotation: X = width, Y = height, Z = initial radian rotation</param>
	/// <param name="endSize">Ending scale and rotation: X = width, Y = height, Z = change in rotation over time</param>
	/// <param name="startColor">The starting color values for tinting. Use Color.White if you don't want tinting</param>
	/// <param name="endColor">The ending color values for tinting. Use Color.White if you don't want tinting</param>
	/// <param name="time">X = Birth time in gametime seconds. Y = lifespan in seconds. Set lifespan to -1 if the quad is static. Default: (0, -1)</param>
	public Quad(Vector3 position, Vector3 velocity, Vector3 normal, Vector3 up, Vector3 startSize, Vector3 endSize, Color startColor, Color endColor, Vector2 time)
	{
		Init(position, velocity, normal, up, startSize, endSize, startColor, endColor, time);
	}

	static void BuildIndices()
	{
		if (m_indices == null)
		{
			m_indices = new int[6];

			//create the indicies for this quad. Note: winding order is in clockwise order.
			m_indices[0] = 0;
			m_indices[1] = 1;
			m_indices[2] = 2;
			m_indices[3] = 0;
			m_indices[4] = 2;
			m_indices[5] = 3;
		}
	}

	/// <summary>
	/// This gets six indicies for this quad.
	/// The indicies can then be inserted into an index buffer.
	/// </summary>
	/// <returns>Six indicies for drawing a triangle list</returns>
	public static int[] Indicies
	{
		get
		{
			if (m_indices == null)
				BuildIndices();

			return m_indices;
		}
	}

	static void BuildVerts()
	{
		if (m_verts == null)
		{
			m_verts = new QuadVertex[4];

			m_verts[0] = new QuadVertex(new Vector3(-1, -1, 0), new Vector2(0, 1));    //bottom left corner
			m_verts[1] = new QuadVertex(new Vector3(-1, 1, 0), new Vector2(0, 0));    //top left corner
			m_verts[2] = new QuadVertex(new Vector3(1, 1, 0), new Vector2(1, 0));    //top right corner
			m_verts[3] = new QuadVertex(new Vector3(1, -1, 0), new Vector2(1, 1));    //bottom right corner
		}
	}

	public static QuadVertex[] Verts
	{
		get
		{
			if (m_verts == null)
				BuildVerts();
			
			return m_verts;
		}
	}

}



#5181090 I need some guidance please

Posted by slayemin on 17 September 2014 - 01:37 PM

Okay... Let's take a step back. (Warning: ample amounts of coffee has been consumed!)

1. It's turn based.

2. It's a card game

3. It's multiplayer

4. It's all in your head at the moment

 

Therefore, the number one first step you NEED to do is get it out of your head and onto something solid. As it stands at the moment, there is no reason this needs to be a computer game at the moment. What you want to do right now is prototype the hell out of this card game. That's really easy for a turn based multiplayer card game. Get a bunch of paper, some scissors, pens/pencils, a couple friends, and make some cards. Explain to your friends how the card game is played and then play it. Does it work? do the mechanics work? Is it fun? Do you actually have a game on your hands? What needs to be tweaked/modified? It's super easy for you to modify all of the game rules when they aren't written into code!

 

Once you have a super fun, well balanced card game prototype working in the physical world, THEN think about creating it on the computer. You'll then seriously know what works and what doesn't work and you won't be scratching your head at the keyboard wondering if a game mechanic works and trying to test out ideas in isolation by writing lots of code which may or may not get thrown away. You'll have the game you want to make, so the rest of the struggle in your development will only consist of writing up the code and the networking, rather than trying to design and develop simultaneously (don't do this! okay?! Design first, develop second! trust me. Doing this will save you months of time and rework!!! You can thank me later.)

 

Let's talk about why you NEED to get this game out of your head and onto physical medium (documentation, spreadsheets, cards, rulebooks, etc). IF you are building this game by yourself, you need to keep your rules straight. If they sit in your head, you will forget. Head space knowledge shifts like the winds, so tamp it down by writing it down. If you don't, you're going to have constantly changing requirements which will be a nightmare to code for. IF you are going to bring OTHER PEOPLE onto the project, then you need to COMMUNICATE to them what needs to be built in very explicit detail! Communication is the word of the day. If there's anything YOU need to do really well, it is communicate your idea perfectly so that everyone understands it exactly the same, exactly how you envisioned it, and everyone on the team knows exactly what needs to be done. None of this can happen if the whole idea is in your head. Nobody is a mind reader. They can't reach in there and figure out what's going on in there, and if you say something, there is a high chance that it can be interpreted wrong. Therefore, super good documentation, preferably a physical prototype of the game, and a way for all team members to talk to each other spontaneously in a face to face setting will be the best thing you can do for your project at this stage.

Chances are also very good that the idea in your head seems to work in your head, but there's this nasty thing about head space ideas: they are like dreams. If you look at them at a quick glance, it all appears rosy and clean. But if you start focusing in on something in particular, it gets murky and uncertain. Determining how exactly that vision should look at the granular level is what separates the pros from the amateurs. Thus, documenting and prototyping the details is to your benefit because it brings up these weak points in the vision and forces you to address them.

 

Once you have all of this done, and only when you have this done, should you start looking at building the game in the digital space.






PARTNERS