Noname 2.5 Shooter (Open source)

Two month ago I took part in a gamejam. Game jam is over, and it time to show what I made.For that gamejam i start rewriting my old prototype SaveShelter, game like wolfenstein 3d. It was cool prototype but, I was new to defold when start it, and it was made for gamejam, so it have some problems and strange code.

When I make that new prototype I try to keep code simple and good.

I still working on base things but first playable version where you can move, shoot and die, done=).

All sources on github. https://github.com/d954mas/2.5d-shooter

Features that already done.

1)Culling. Have go only for visible walls,objects.SaveShelter have problems with big levels. It was easy to get sprite or go limit. In new version only visible objects,walls will have go.

2)Cells color. Every cell can have a color. All objects in that cell and near wall will mix their color. SaveShelter have that feature but only for 16 colors for performance reason. Now any color can be used.In runtime game will create a lightmap texture and in shader mix colors.It work good for wall. But objects and enemies can be in two cells. For them use sprite.set_constant. It is bad because it break batching.But because theare are only visible go, it is not so bad.If some one have idea how to make it, better please tell me. :thinking:


3)Fog. In shader. Can change distance and fog color.
4)Physics. Base colliders for walls and shoots.
5)Camera. Use rendercam. Rendercam is simple and awesome.
6)Pathfinding. Micropather,a c++ pathfinding lib.I have a lightweigth map copy in native, for pathfinding. Worked good and fast.
7)Mouse lock. For that I use defos.It can lock mouse but, locked mouse do not send input for defold :disappointed_relieved: . But there is a workaround. Hide mouse, and every frame move it position to center. Defold still do not get input, but you can save dx,dy and use it later.

if not IS_HTML then
    if M.locked then
        local x,y = defos.get_cursor_pos_view()
        M.cursor_movement.x = x-RENDERCAM.window.x/2
        M.cursor_movement.y = y-RENDERCAM.window.y/2
        defos.set_cursor_pos_view(RENDERCAM.window.x/2, RENDERCAM.window.y/2) -- In game view coordinates
    end
end

8)Pause modal. When you unlocked you mouse, game will be paused.I use my own scene manager. It was made about year ago. Few days ago I find that it not worked it release bundle :grin: . I use tostring(url) as a key in table.Do not do it.Use tostring(url.path).
9)Pickups. Pistol ammo and hp.
10)Simple enemy. Simple state machine.It follow player and hit in melee.
11)Weapons. I do not know the best way to do weapons. They can have different combinations of parameters, and OOP is not good for that. So I split weapon to weapon_base(logic) and weapon_prototype(config). Weapon_base use weapon_prototype and do some logic.

PISTOL = {attack_type = M.ATTACK_TYPES.RAYCASTING,ammo_type = M.AMMO_TYPES.PISTOL,target = M.TARGET.ENEMIES,
raycast_max_dist = 10, reload_time = 0, input_type = M.INPUT_TYPE.ON_PRESSED, player_weapon = true,
animations = {idle = hash("pistol_1")}, tag = "PISTOL";,
sounds = { shoot = SOUNDS.sounds.game.weapon_pistol_shoot, empty = SOUNDS.sounds.game.weapon_pistol_empty },
first_shot_delay = 0.1,shoot_time_delay = 0.4, damage = 25
}

12)Level editor. Defold editor is not good for that types of levels. So I used tiled. Level from tiled will saved in lua. Then I convert that lua file to my own format. I need my own format, because I need make some checks and precalculations. Also tiled output is contains useless and duplicated info. For example every level have tilesets description which is same for all of them.


13)Luacheck. Static code analysis. It help to keep code clean.

14)Tests. I newer made tests before. But it time to start.Use deftest for that. Now I have few basic tests.For exampe test that check that all levels can be loaded.

16)Transparent cells. For example vines.

17)ECS. Pattern for game logic.The main idea to use entities(bag for components), components(variables,for example position component) and systems(logic).System iterate entites that have some components, and do some work.So instead of having complex logic in one or few scripts. I have dozens of systems.

local ECS = require 'libs.ecs'
---@class MovementSystem:ECSSystem
local System = ECS.processingSystem()
System.filter = ECS.requireAll("position","velocity","speed")

---@param e Entity
function System:process(e, dt)
    e.velocity = vmath.length(e.velocity) ~= 0 and vmath.normalize(e.velocity) or e.velocity
    local movement = e.velocity * e.speed * dt
    e.position.x = e.position.x + movement.x
    e.position.y = e.position.y + movement.y
end


return System

18)Addaptive fov. Fov changed for differents screen sizes.
19)Resizer. Use defos and mnu to change game screen size.It is similar to simulate resolution from editor.
%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9
20)Weapon bobbing.

25 Likes

Wow, this is impressive! Well done!

2 Likes

Put each color ID on its own render predicate, and then pass the color value when you render as a property. This should reduce drawcalls to one per color ID instead of one per object. You will want distance/fog shading done in the shader, and to use different colored sprites for extra shading effect.

Other engines have better ways of dealing with this…

1 Like

I use different ptedicates for colors in save shelter. It worked, but I don’t like that way. It is big limitation.

1 Like

Maybe instead of painting tiles could use a light position/color based system. Then you set the light/colors in the render script passed as properties still, but it’s to all objects at once. Then you have one draw call and you only send lights near the player.

1 Like

I don’t understand you) What you mean?

I need center of sprite for objects. If in shader we have gl_VertexID, it will be easy. But we don’t have it(

Impressive work! Thanks for sharing :space_invader:

2 Likes

Very cool! How did you solve html5 mouse locking?

1 Like

It was easy. I use defos. :grinning:

defos.on_click(function ()
    if M.locked then
        defos.set_cursor_locked(true)
    else
        defos.set_cursor_locked(false)
    end
end)
2 Likes

What was the difference between html5 and desktop mouse locking?

1 Like

Defos have method for lock cursor defos.set_cursor_locked(true).But it worked in different way for html and pc. In html you will get on_input when move mouse, but in desktop don’t.
So for html i lock cursor and use on_input.

function M.on_input(action_id,action)
    if IS_HTML then
        if action_id == nil and defos:is_cursor_locked() then
            M.cursor_movement.x = action.dx
            M.cursor_movement.y = action.dy
        end
   end 
   ...

For desktop i don’t lock cursor.I clip it in game window. And move cursor back to center every frame. When i move it, i saved dx and dy.

function M.update_cursor_movement()
    if not IS_HTML then
        if M.locked then
            local x,y = defos.get_cursor_pos_view()
            M.cursor_movement.x = x-RENDERCAM.window.x/2
            M.cursor_movement.y = y-RENDERCAM.window.y/2
            defos.set_cursor_pos_view(RENDERCAM.window.x/2, RENDERCAM.window.y/2) -- In game view coordinates
        end
    end
end

function M.lock_cursor()
    if not M.focus then return true end
    M.locked = true
    M.cursor_movement = vmath.vector3(0)
    if not IS_HTML then
        defos.set_cursor_visible(false)
        defos.set_cursor_clipped(true)
        M.update_cursor_movement()
    end
end
7 Likes

Perfect, thanks!

2 Likes

Hello, awesome project! What libs do you use for ECS in defold?

1 Like

I use tiny-ecs https://github.com/bakpakin/tiny-ecs
With some modifications.

1 Like

Show shooter to another devs. They said that in fps movement acceleration is not instantly. There are some acceleration,inertia and etc. Is someone make that logic, and can give advices how the best way to do that.
I add acceleration on deacceleration. It better then instant move, but i am not sure that i doing it right :thinking:
I make two types of accelerations.
One change velocity when change camera direction.
Second change velocity only when keyboard input is changed.

Also need some feedback =) :grinning:
Update html version. https://d954mas.itch.io/noname-25d-shooter
Press 1 to change acceleration type.

--normalize keyboard input
if e.movement_direction.x ~= 0 and e.movement_direction.y~=0 then
    e.movement_direction = vmath.normalize(e.movement_direction)
end
--first type. Inertia when change camera direction
if not toggle_1 and  e.angle then e.movement_direction = vmath.rotate(vmath.quat_rotation_z(e.angle.x),e.movement_direction) end
local target = e.movement_direction * (e.movement_max_speed or 3)
local accel
--Take the dot product of target to see if the player is moving according to target
if vmath.dot(target,e.movement_velocity) > 0 then
    accel = e.movement_accel or 3
else
    accel =  e.movement_deaccel or 6
end

e.movement_velocity = vmath.lerp(accel * dt, e.movement_velocity,target)
local vel = vmath.vector3(e.movement_velocity)
--second type Inertia only when change direction(keyboard keys)
if toggle_1 and e.angle then vel = vmath.rotate(vmath.quat_rotation_z(e.angle.x),vel) end
e.position.x = e.position.x + vel.x*dt
e.position.y = e.position.y + vel.y*dt
1 Like

Have spent a lot of time on thinking about game and it features and start writing concept doc of game. Also, made some fixes and implement some features.

Trying to work on game every day. I do not have a lot of time because of work, but i think if i will do a lot of small steps every day, sometime game will be done.

Bought two new monitors. So now I can run ide and defold editor in different monitors. x1.5 to productivity :grinning:

Changelog:
1)Culling for pickup sprite.(sprite is destroyed if go is invisible)
2)Remove invisible wall sides when parse level. When walls stay near there are some sides than player can’t see, so game do not need to draw them
3)Fixed bullet ignore walls. You can kill enemy throw wall,forget to add obstacle mask to bullets :grinning:
4)Add simple level selection scene
5)Add simple game_over modal. Can restart or go to main menu
6)Add simple pause modal. Can resume or go to main menu
7)Fixed weapon raycast. Raycast is nil if player in inside enemy collider. So in close range raycast was nil, and shot was missed.
8)Add more tests.(pathfinding, parser)
9)Flash enemy when it takes damage
10)Add camera shaking when player receive damage. Like here https://www.youtube.com/watch?v=tu-Qe66AvtY
11)Try make weapons. I do not like how I made them, so it will be refactor =)
12)Add test blood particles for enemies.
13)Code cleanup, refactoring and simple fixes=)
14)Change tilesets parsing. Now tilesets in level map can be in any order.(Tiled use global id for tile, that id depends on tilesets add order to map.)
15)Wall textures can be any size. So I can change wall texture to bigger texture in any moment.Also, I can use 64 and 128 sized walls is one level.
16)Add thin wall(only visual no collisions)
17)Add keys and doors.
18)Add travis(CI). Only start with it. I want to run test and made bundles in ci. For now, it downloads bob. And make headless(for test) and html bundles.
https://forum.defold.com/uploads/default/original/3X/8/d/8d635f070a65d4f9092c0cb36a72d07080d28faa.mp4

9 Likes

I am return to that project=).
Half a year I did nothing on the project =).
I switched to another project, but now back again.

2.5 weeks it took:
1)To remember how it works and what happened inside
2) Updating libraries.
3)Replace old scene manager with new version(prev was made 2 years ago, and contains a lot of strange things,and hotfixes)
4) A refactoring of the walls render (so that they can be easily modified during the game, for example, for animated walls).I use ecs pattern. And in prev version walls was not a entity, it was separate object.Now wall is entity.
5) I threw out the built-in physics. Now I use https://github.com/DanielChappuis/reactphysics3d and for now everything is ok. Also added debug render for it.
6) Add debug panel.
7) Game was downgraded. Threw everything except walls, characters and walk. I will return all of it=).

4 Likes

Yay!

Why?

2 Likes

Current physics is ok, and i can do all things that i need with it, but it was not comfortable for me.
I want to have more control on physics bodies. When create it and in runtime.

1)It need a script for object to handle physics collisions. But i prefer to use lua modules instead of script. I only have one script for every scene(scene controller), all other logic in lua modules.
2)Limit for 1024 scripts. If i add physics script for every wall, 1024 was to small for me. So i add script only for player, and enemy. But if i need more object, for example boxes that can be moved, bullets that ricochet from walls like grenades or lazers, it will be very easy to break that limit.
3)I want to change mask and group in runtime.
4)Problems with scale.When i want to change size in runtime.
5)Problems with scale.When create from factory. Not remember what it was. Mb it was not scaled when create from factory, or mb i can’t use different scale for different direction. 1 for y and 10 for x to make rectangle instead of cube.
6)I received one more collision_responce after physics was removed. All my pickups was handled two times=).
7)If player rect, inside enemy rect i do not get that collision in raycast response.
8)Can’t make raycast that give me all object in line.
9)Physics updated in next frame. I can’t move object then check that everything worked.
10)I am not sure how physics will be worked for big speeds and small walls. Will be bullet ignore walls or not. In custom physics, i can update it few times per frame, to fix it.

3 Likes