How to make your weapons look better (and common mistakes to avoid)

Handy guides on how to do things, written by users for users.

Moderators: GZDoom Developers, Raze Developers

Forum rules
Please don't start threads here asking for help. This forum is not for requesting guides, only for posting them. If you need help, the Editing forum is for you.
User avatar
Jekyll Grim Payne
Global Moderator
Posts: 1109
Joined: Mon Jul 21, 2008 4:08 am
Preferred Pronouns: He/Him
Graphics Processor: nVidia (Modern GZDoom)

How to make your weapons look better (and common mistakes to avoid)

Post by Jekyll Grim Payne »

This mini guide is meant to address a number of things that are often overlooked when designing weapons, both in terms of graphics and coding, and using these tricks, in my opinion, can improve the look of your guns a lot.

Note, all images are clikcable links.

* Change the weapon’s bobstyle.
You may or may not like vanilla Doom bobbing (I personally don’t), but if one thing is certain, it’s that it’s VERY recognizable. It’s extremely obvious when your weapon bobs just like in Doom.
Changing this is easy: just modify the Weapon.BobStyle, Weapon.BobSpeed, Weapon.BobRangeX and Weapon.BobRangeY properties. I recommend smaller range, increased speed, and InverseSmooth or InverseNormal bobstyle.
If you want to go the extra mile and you’re using a custom PlayerPawn, override its BobWeapon() virtual function to change it however you like.

* Do not have cutoffs in angled sprites.
Angled sprites are great! I love the angled perspective. But remember: if you make horizontal sprites, they must NEVER cut off along their width. Keep in mind that monitors can be 16:9, 16:10, 21:9 and even 32:9, and nobody knows what the future holds. If you add cutoffs to your sprites, those cutoffs WILL be visible on somebody’s monitor. Just let those sprites stretch until the whole body of the gun is visible horizontally.
Additionally, I recommend extending your sprite vertically as well: it’ll give you more freedom when applying offsets to it.



* Make sure your angled sprite is actually pointing at the crosshair.
Again, angled sprites are great. But they only work if they imitate perspective properly, and that, among other things, requires that the whole sprite aims at the crosshair (i.e. the center of the screen). With the aspect correction, figuring out the right offsets can be difficult. Sometimes you’ll have to hide a lot of the gun below the screen because you initially drew/rendered too much of it, but it’s a sacrifice you’ll have to make, because if the sprite isn’t aiming at the center of the screen, it simply doesn’t look right.



* When designing recoil or melee attack animation, the initial movement must always be faster than the return movement.
When this principle isn’t followed, your movement will convey no momentum or force, and it always looks obviously off.
When you fire a weapon, it jerks in your hand FAST. Then the shooter’s hand returns the gun to its rest position, and that return movement should be at least 2 or 3 times longer than the initial recoil movement.
Similarly, when you throw a punch, your arm moves forward much faster than when you move it back, to its rest position. If there’s no difference, it’ll look like the character is just moving their arm back and forth without applying any force.
Don’t be afraid to skip frames on the initial movement! You can use as little as 2-3 frames to move the gun from rest position to the furthest back position, but twice or thrice as many to return it.

(This is an animated GIF, click to see it)


* Create your own base weapon class.
In most weapon mods, you’ll want a lot of specific behaviors and features that apply to all weapons. This is ESPECIALLY true in ZScript, which lets you design custom functions. Do not base your weapons on the Weapon or DoomWeapon classes. Make your own base class, add custom behavior to it, and have all your guns inherit from it.
This logic should be applied to most classes, in fact. Make your own base actor, base item, base ammo (by overriding its GetParentAmmo() virtual function).

P.S. By the way, DoomWeapon doesn’t define anything special besides Weapon.kickback.

* Use overlays.
You can use them for anything. You can draw your muzzle flash with A_Overlay instead of A_GunFlash and place it below the gun rather than above it (this will save you the trouble of having to cut out the shape of the gun from it). You can use them for independent moving parts, blinking lights, or anything else.
Among other things, using overlays lets you illuminate and even fade in/out elements of the gun. No more bright keyword that give your sprites a boring, washed-out, fullbright look.

(A bit of an extreme example from Painslayer, but each layer is, in fact, moved and scaled separately, and some are even rotated)


* Draw muzzle flash/muzzle highlights on separate layers.
Avoid drawing them directly on the gun. It’s a lot cleaner and gives you more control when the flash is on a separate layer. This way you can control its lighting to make it look nice in dimly-lit environments, and you can freely swap/randomize the flash. Just use A_Overlay and A_OverlayFlags to modify the flash’s opacity and renderstyle.

My one-image guide on creating muzzle flashes https://i.imgur.com/wv7zOzI.png and muzzle highlights https://i.imgur.com/2wN6pKR.png

* Use more than one image for the muzzle flash.
When there’s only one image, it’s actually pretty noticeable—especially on fast-firing weapons that show this flash frequently. It’s obvious, it’s a bit annoying, and can trigger OCD in some people.
Use 3-5 images and pick them randomly each time the flash is drawn, and/or use overlay scaling and rotation to modify its look without having to add a gajillion sprites.

(This is an animated GIF, click to see it)


* Take pixelratio into account. Everything you see in Doom is, by default, vertically stretched by the factor of 1.2. If you don’t take this into account while designing your gun sprites, or don’t scale them in the game, your weapon will appear stretched, not the way it looks in your image editor/render window. This is ESPECIALLY obvious on guns with round elements, like scopes.
Read more about pixelration on the Wiki: https://zdoom.org/wiki/Aspect_ratio_correction
Correct the stretch by using Weapon.WeaponScaleX and Weapon.WeaponScaleY options or the TEXTURES lump.



* Use A_WeaponOffset/A_OverlayOffset.
These are extremely useful functions that will let you cut down on the number of sprites (no more copying the same sprite with different offsets!) and animate offsets programmatically. It’s a great tool for recoil, selection and deselection animations.

* Use the other overlay functions.
Namely, A_OverlayScale and A_OverlayRotate (you will also need A_OverlayPivotAlign to modify the anchor point of the sprites). Again, these functions are great for creating programmatic animation: without creating extra images, they let you rotate and scale your gun. Smart use of those functions can sometimes produce convincing gun animations with just 1-2 sprites!

* If you use bullet tracers, make them invisible for a couple of frames.
Bullet tracers are super common in video game weapon design. GZDoom offers an easy way to set them up by spawning visual projectiles with A_FireBullets. However, if you just spawn a projectile with the correct model, oftentimes it’ll look like it appears from below/behind your gun—because projectiles are coming out of your eyes! There are various ways to work around this, but one of the simplest solutions is to start your projectile’s Spawn sequence with TNT1 A 2.

Even better results can be achieved by using custom functions that offset the tracer actor in front of the player (but that will require some scripting and math).



* Create a custom puff.
Bullet puffs are great, and it’s best that you don’t stick to the vanilla BulletPuff class. Make it cooler, add animations to it, spawn some debris or particles. Players will see it all the time, it’ll enhance the look of your weapon.

* Use dynamic lights for weapon light.
This falls more into the category of style, but unless you’re aiming for a specific retro look, functions like A_Light1 are not the best: they just add flat illumination to the whole level.
Using A_AttachLight, you can dynamically spawn dynamic lights at player’s viewheight, and even dynamically control their brightness/radius/color (for example, to match your muzzle flash), with pretty nice results.


You can learn more about weapons and sprite layers in the Weapons, Overlays and PSprite chapter of my guide, ZScript Basics.
Last edited by Jekyll Grim Payne on Sun Sep 24, 2023 6:18 am, edited 1 time in total.

Return to “Tutorials”