Creating a fancy weapon slot system for my game

Started by
12 comments, last by Alberth 8 years, 6 months ago

Hey again people! I realize I have another topic on animation on here too, but I find that working on problems in parallel seems to spare me on. I have a weird way of being productive, but it seems to work for me :) I have limited programming experience, but despite that, I've been working on a prototype for a game for about 3 years, and it's going surprisingly well! I'd also like to add that I'm open to suggestions, but I'd like to keep them within my grasp (CS graduate with 3 years hobbyist XNA/Monogame experience (3 years total)), and also not completely rip my code apart and start from scratch, even if there are "better" ways of doing it. However, I'm very open to making changes to my current design pattern.

So, all players in my game have 3 empty item slots. At the beginning, they can equip any 3 items from a randomly drafted item pool and use them any combination that they see fit. Balancing this will be a whole world of pain, but I'll worry about that later! There are currently 5 types of item: Passive, Activate, Deploy, FixedGun and PivotGun.

-Passive items obviously affect the player if they are simply equipped for the duration of the round.

-Activate items create a simple effect, then have some effect on the player or surroundings - also given a cooldown time.

-Deploy items drop an entity into the GameSpace, such as a turret, defensive barricade, or minion, for example.

-FixedGun items are guns that only fire forwards or in a set direction/pattern (think Megaman/Metroid/Castlevania).

-PivotGun items are guns which will rotate and aim at the cross-hair. The bullets will move at an angle and are aimable with the mouse/right analogue stick (Think Abuse/Capsized).

I've currently designed it so that with any player, any item can simply be loaded in with an arsenal-changing method whenever the player starts the level or enters a shop. This is working fine at the moment, and the under-the-bonnet code makes it very easy to create new items, so that if I do create one, everything required is in a single CS/JSON/XML sheet and no other code in the whole solution has to be changed. Obviously, it's nearly always impossible to predict any future hitches they maybe come up, but I've been constantly tweaking my hierarchy and would like to consult you guys to see if it makes sense:

hier1_zpsevgpzfq1.png

(I mixed up FixedGun and Gun in my diagram. Whoops! FixedGun inherits from Gun, so just swap the two names around when looking at the above diagram)

I've made a few types of items to test this hierarchy and account for future changes and they seem to be working well. When the guns are fired (FixedGun and PivotGun), they generate the bullets accordingly (Which inherit from BaseProjectile), then these bullets are added to a static BulletList in the GameSpace. I kept this list static and public since there will only ever be one instance of the GameSpace at any given time. These bullets are then checked against every Entity nearby (I implemented spatial hashing). This allows very interesting tactical combinations. For example, if I choose to un-anchor the shop, players can use weapons with knockback to shoot the shop around, and it will collide with tiles and bullets. Force can also be applied to existing bullets, since the BaseProjectile itself inherits from the Entity class. This allows bullets to effectively hurt players or enemies, destroy certain props or move the props, or for projectiles to hit other projectiles (a force gun could blow mines away, for example). I want to keep this consistent, because I like a massively interactive environment, and don't want anything to be "hacked" in and miss out on this web of co-interactivity. This also gives a cool "domino" type effect. So, as another example, I could hypothetically place a jump pad on the floor, then place mines, then shoot the mines at the jump pad, and the mines will constantly bounce on that pad or into the next one, giving a seamlessly transitioning GameSpace.

Also, despite the items working well, I've really struggled with setting up the PivotGun weapons, and haven't finished implementing them yet. I want the bullets to do a number of things:

-Bullets originate in the weapon's nozzle

-Bullets start at the same angle as the weapon's angle when fired

-Bullets move towards the cross-hair

-A muzzle flare effect to originate over the nuzzle when fired and be attached to the end at the correct angle

The diagram below shows how I effectively want it to play out:

hier1_zpszpw8jwmv.png

As it stands, I've got the bullets moving from the player's shoulder to the cross-hair. The gun also aims at the cross-hair, but in this fashion:

hier1_zps2rrrjb37.png

In this current state, the aim of the gun is not taken into consideration, as its positioning is offset from the player's arm. It's worth noting that I draw my guns WITH the arm attached in the PNG file, as I like to my my player's arm position differently depending on the gun. The bullet travels as intended, and the aiming works to an extent, but I'd like the actual end of the gun to aim toward the cross-hair and for the bullets to originate inside the nozzle (like an actual gun). I considered giving the character a completely straight arm with the gun centered every time, but this really limits my creativity :(

Here's a bit of pseudo for how the bullet is created:


//Assume everything else is working fine
//Little code should be passed into the actual bullet

            Vector2 gunPosition = position + gunOrigin;

            direction = new Vector2(mouseVector.X, mouseVector.Y) - gunPosition;
            direction.Normalize();

            if (spriteEffect == SpriteEffects.FlipHorizontally)
            {
                gunRotation = (float)Math.Atan2(-(double)direction.Y, -(double)direction.X);
            }
            else
            {
                gunRotation = (float)Math.Atan2((double)direction.Y, (double)direction.X);
            }

//Rotation should account for nozzle position

//startingPos must be inside the nozzle
//Nozzle need to be attached to end of gun
startingPos = new Vector2((int)nozzleRect.X, (int)nozzleRect.Y);

spriteBatch.Draw(singleRedPixel, gunOrigin + manualOffset, angle, etc)...
//Draws a red rectangle over what I define as the nozzle

if (gunFired)
{
     bullet b = new bullet(startingPos, gunRotation, velocity)
}

This is in its simplest form. I'd like to have it so every time I create an instance of a PivotGun, I can manually feed in an offset for the nozzle, then the gun can aim and bullets can originate based on that. My trigonometry is very rusty though!

So, I guess, based on that, my 2 questions are:

1.) Is my hierarchy / pattern design decent?

2.) How would I add this new behaviour to cross-hair-aimable guns? (Has to take into account varying bullet sizes and gun shapes/sizes)

Help would be greatly appreciated :)

Advertisement

For pointing the gun such that the arm and gun rotate and gun ends up pointing in the aim direction...

Maybe find the vector that represents the shoulder to the back of the gun(distance) and the vector that represents the path from the back of the gun to the nozzle.

Then rotate them as an adjoined pair of vectors toward the aim vector. This could get tricky if the aim is suddenly on the opposite side (in which case you'd want to find the shortest angle between gun-barrel-direction vector and aim position (where aim vector in this case is vector between back of gun and target). Then once the gun is rotated at the appropriate distance toward that aim angle (could use a smoothing technique like newAngle = 0.3f * angle_difference; //30% current difference to smoothen(ease) out rotation changes)

Then you would rotate the arm so the hand is positioned over the handle (the handle should be the appropriate distance cuz we rotated at a distance that we expect the back of the gun to be at). One thing you can do with this is find the vector from shoulder to new handle position and use the vector's angle to assign the current arm rotation.

It works in my head anyway - lol ;)

Btw to add the bullets at the nozzle you would use something like this:

Quaternion aimQuat = Quaternion.CreateFromYawPitchRoll(0, 0, rotation); // rotation of gun
Vector2 bullet_offset = Vector2.Transform(gun_nozzle_offset, aimQuat); // (ie: gun_nozzle_offset = Vector2(30, -2) )
bullets.Add(new Bullet(pos + bullet_offset, vel+aim_direction*bullet_speed)); //add bullet at gun at correct velocity (player velocity + bullet velocity)

Something like that anyway - although it may require some alteration to fit with your code.

How about grenades? Knives? Spears?

Most game engines I've worked with handled it through data, not code.

Things that can be held have a named mount point in the model. Models have multiple mount points that can be used for different things.

A tiny pistol, a rifle, a shotgun, a rocket propelled grenade launcher, they've all got different details. Most of those details can be addressed through data, not through subclassing.

Data can specify that model X mounts on mount point "long_gun_slot", has a set of named animations for pickup, drop, carry, and shoot, has a data value that shows where the reticule is supposed to be, and more.

All of that is data, not code.

@frob

I was unaware of this, Frob! I'm taking a different approach because I won't be designing weapons in that fashion. I don't

want a large list of items with different stats/skins, but a large list of items with their own unique abilities. I won't be re-hashing

the same weapon twice, for example, like having an M14 and AK47. The spreads, fire rates, etc, are varied, but it's essentially

the same type of weapon that doesn't need re-hashing multiple times. I prefer the Zelda-type approach, where the game isn't

saturated with mildly-varied items, but a few ones that are totally unique. I'll only be having two assault rifle weapons, one

which creates stairs and another that fires trap-like rivets. The actual stats of each weapon are still loaded in using data.

Could this still be improved in your opinion?

@renman

Thanks for the approach! I'll be looking into quaternion maths to see if I can come up with an elegant solution. Once I crack this,

it'll work for all the guns I've been working on, and it'll be really rather exciting :D

I agree that data based determination is often the best route, although...

one might caution sometimes direct coding is actually a better(faster) approach where data based can in and of itself complicate code more than it otherwise would need to be for the given scenario. There are often occasions that I have seen where it is actually far simpler to code the desired functionality than to create external entities which need to be loaded and understood by the code. In fact I've seen ppl (and done it myself) put together editors/interpreters where it actually takes longer to edit the entities in the gui (and must save and load and be deciphered) than to simply enter the needed data directly into some cleverly designed Macro/function/method.

I try to avoid superfluous coding -- saves a lot of time, less to look at, and easier on the eyes. Sometimes direct is less work and less code.

I actually ended up scrapping some of these external approaches in favor of direct coding since it was actually much faster productivity and easier to test. Why build script systems, editors, etc -- when direct source code is (sometimes) faster and easier? Sometimes it is [ in which case the source code is the editor (not the best for teams tho) ]

For example, I actually made a flash animation type system in which everything is built entirely with source code(methods combos) which actually turned out to provide precise results and fairly fast production. ( I don't recommend this for everyone )

Whatever is faster in the long run.

Does anybody have any more input on correctly positioning the gun nozzle? Haven't really been able to crack the problem.

I'm keeping my guns code-driven for now, as I don't really see how using data could possibly help!

Assuming it was data driven, the "nozzle" could be specified for each item.

If each one has totally unique behavior as you wrote above, and each completely-unique items is being hard coded, then there is no commonality and it needs to be coded into every item individually.

Does anybody have any more input on correctly positioning the gun nozzle? Haven't really been able to crack the problem.

Oh, didn't realize it's such a fun trigonometry problem smile.png

[attachment=28936:angle_gun.png]

Above a display of what is happening. The black dot is the shoulder position, with brown arm and gun, where the gun is aimed at the cross hair.

There is a vertical offset between the line of the gun firing, and the posistion of the shoulder of length "da" (distance arm). If you would rotate all around, you get a circle with radius da.

The cross hair is dx pixels horizontally and dy pixels vertically away from the shoulder.

That makes dd = sqrt(dx*dx + dy * dy). Also you can compute alpha = atan(dx / dy) near the cross hair.

Using dd and da, you can compute beta = acos(da / dd).

The angle near the cross hair between the gun line and the line dd is thus pi/2 - beta. The angle of the gun-line and the horizontal line is therefore pi/2 - alpha - (pi/2 - beta) = pi/2 - alpha - pi/2 + beta = beta - alpha.

Edit: After posting I realized you can compute the angle between the gun-line and dd directly by asin(da / dd)

Most games don't bother with that.

The data can be placed directly in the model or in the weapon's data.

Some shoot with a reticle, drawing a path from the center of the viewport through the reticle, with the reticle's position on the screen specified in data. Animation data for walking or running allows the reticle (and the bullet vector or launch position) to move along an animated path.

Others use the model location of the gun, it is easy enough to have a named vertex in the model that points wherever the weapon points. A single ray casting shows what will get hit in a straight line, or where to put the targeting icon.

Thanks for putting so much consideration into the diagram, Alberth. I've brought over a friend who's solid in maths

but has little programming experience, so we've been trying to bounce ideas back and forth.

This is the current code I have, but it seems to still be playing up. I would like the middle of the muzzle box to join

with the middle of the cross-hair. We were almost onto something, but I kind of feel like admitting defeat. Here's the code we

have so far (C#):


private void AimingRotation()
        {
            //All this method does is cause the gun's nozzle to aim at the direct centre of the cross-hair.
            //position is the player's position, and gunOrigin is vector that keeps the arm with the gun attached to the player's shoulder
            //gunPosition is the actual position of the pivot that anchors to the shoulder that the gun rotates around

            Vector2 gunPosition = position + gunOrigin;

            //THESE SHOULD BE PARAMETERISED:
            //I will eventually make these variables defined through child classes
            deviation = new Vector2(36, -4);
            int muzzleWidth = 8, muzzleHeight = 6;

            Vector2 dx = gunPosition + new Vector2(deviation.X, 0);
            Vector2 dy = gunPosition + new Vector2(0, deviation.Y);

            Vector2 temp = gunPosition + deviation;
            double length = temp.Length();

            double tempanswerfirstone = deviation.Y / length;
            double tempanswer = Math.Asin(tempanswerfirstone);

            float degrees = MathHelper.ToDegrees((float)tempanswer);

            direction = new Vector2(mouseVector.X, mouseVector.Y) - gunPosition;
            direction.Normalize();

            if (spriteEffect == SpriteEffects.FlipHorizontally)
            {
                gunRotation = (float)Math.Atan2(-(double)direction.Y + tempanswer, -(double)direction.X + tempanswer);
            }
            else
            {
                gunRotation = (float)Math.Atan2((double)direction.Y + tempanswer, (double)direction.X + tempanswer);
            }

            //Shouldn't be able to touch this manually - parameters should accept deviation changes at start
            muzzleRect = new Rectangle((int)worldRect.X, (int)worldRect.Y, muzzleWidth, muzzleHeight);

        }

Something is slightly off, and we just simply can't figure out what it might be. I have radians and degrees to work with and I'm not sure

where to add them. Also, when the character faces left, the whole thing is completely off. Does anybody have any suggestions? :)

This topic is closed to new replies.

Advertisement