Noname 2.5 Shooter (Open source)

#1

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.

19 Likes

#2

Wow, this is impressive! Well done!

2 Likes

#3

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

#4

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

1 Like

#5

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

#6

I don’t understand you) What you mean?

0 Likes

#7

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

0 Likes

#8

Impressive work! Thanks for sharing :space_invader:

2 Likes

#9

Very cool! How did you solve html5 mouse locking?

1 Like

#10

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)
1 Like

#11

What was the difference between html5 and desktop mouse locking?

1 Like

#12

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
6 Likes

#13

Perfect, thanks!

2 Likes

#14

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

1 Like

#15

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

1 Like

#16

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

#17

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