Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411500 Posts in 69373 Topics- by 58429 Members - Latest Member: Alternalo

April 25, 2024, 02:04:59 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsCaracal: a dark space salvage roguelike (platformer)
Pages: 1 [2] 3
Print
Author Topic: Caracal: a dark space salvage roguelike (platformer)  (Read 14836 times)
manish.spangle
Level 0
*


View Profile
« Reply #20 on: February 12, 2014, 03:13:45 AM »

Nice. Looks good!
Logged
{VeTeR}
Level 0
***


The Dark Matter


View Profile WWW
« Reply #21 on: February 12, 2014, 03:52:19 AM »

The art design and that is supports Tiled editor makes me want to make maps for it  Well, hello there!
Logged

FraktalZero
Guest
« Reply #22 on: February 12, 2014, 09:36:00 AM »

Oh hey, I saw your message last night but forgot to respond! That definitely helped me get a better handle on my character lighting Smiley. Thanks man!

No problem!
Logged
lobstersteve
Guest
« Reply #23 on: February 23, 2014, 02:35:31 PM »

i'll follow this  Smiley
...new charakter fits well
Logged
Udderdude
Level 10
*****


View Profile WWW
« Reply #24 on: February 23, 2014, 02:51:36 PM »

Looks cool, but I kinda prefer the old character ..
Logged
kcbanner
Level 0
***


View Profile
« Reply #25 on: April 09, 2014, 06:52:02 PM »

Update time!

A playable demo is available here!

I finally got my scripting system to the point where I could add enemies! This meant refactor the existing components that I used to build the player (BodyComponent for movement, and AttackComponent for attacking) to accept inputs provided by other components instead of user input directly. Then I hooked up a ScriptComponent to provide inputs based on some very simple (for now) AI.

I also fixed up my lighting renderer to allow up to 256 simultaneous lights at almost no extra cost. I was able to add fully dynamic lights to each laser projectile.

Here is the first enemy in action (click for smooth video!):






As for gameplay, I think I've come up with the basis for the game: The player is a ship salvager, so their job is to clear ships of enemies and salvage/scavenge any valuable parts, items, etc that they can from it.

There will be permadeath on a per-ship-run basis. What I mean is that if the player dies while clearing a ship, they start back on their ship, as a fresh clone of themselves. The idea here is that the player has been constantly cloning themselves over and over as part of their job. This way I can provide the excitement of a permadeath roguelike while providing some persistence. If the player completely finishes a ship run, they keep the rewards, experience, etc. Every time the player successfully makes it back to their ship, their progress is snapshotted for future clones. Rogue legacy did this really well with their castle leveling system.

I think I will also have some traits that only persist on a single clone so that there is still that roguelike incentive to keep the current run alive. Maybe the clone has gathered robotic implants which increase damage or skills, and these of course get lost when their physical body dies.

I think the clone theme will tie into the story nicely. I'd like the player initially to just clear ships filled with simple enemies like drones or bandits. Once they have fully cleared a few though, I want to start bringing the player's past clones in as enemies. Since the player started as a clone, they have no idea how many other older clones are out there.

I like the idea of the player's past clones going rogue and not returning back, either because they went nuts in space or just wanted to steal whatever ship it was and take the salvage for themselves.

This could end up in some really cool scenarios; imagine starting a new ship run only to find that the ship is filled with cultists and your clone is the leader. Or finding a bloodbath left behind by something and getting to the end of the ship and finding a corpse of your own clone. I think this would really work with the creepy/eerie/alone in space feel I want to give the game.

Anyway, now I need to come up with name for this thing. My working title has been "Caracal", as just a random code name that I used when I first started building the engine. I think that title might work as "Caracal" is a good ship name and could be worked in to the story somehow, but I also think the title should be more descriptive.

Let me know what you think of these story ideas!
« Last Edit: April 09, 2014, 07:14:38 PM by kcbanner » Logged

gumustdo
Level 0
***

Swinging pencil.


View Profile
« Reply #26 on: April 09, 2014, 07:05:22 PM »

Wowww, look at those colored lighting! This is so amazing in minimalist graphic. That rim light on the floor just pretty neat.

I like how you re design the character too. Now he's more stoic which is good match with the dark atmosphere.
Logged

Twitter @gumustdo
Gueib
Level 1
*


don't be jealous of my boogie


View Profile WWW
« Reply #27 on: April 17, 2014, 08:21:41 PM »

Looks great. I'm interested to see where it goes.
Logged
SolarLune
Level 10
*****


It's been eons


View Profile WWW
« Reply #28 on: April 18, 2014, 07:34:46 AM »

Yeah, this looks cool. I like the new character, as well as the original. In any case, action platforming rogue-likes are nice. As a question, do you have a shield that rebuilds over time or something, or do you just have to eat those shots from your first enemy?
Logged

dotsquid
Level 0
**



View Profile
« Reply #29 on: April 22, 2014, 04:10:33 AM »

Hey, the demo crashes with access violation exception (C0000005) during launch. I hear some background sound while the window is still white and then the crash happens.
Windows 7 x64, MSVC2012 redist is present.
Logged
kcbanner
Level 0
***


View Profile
« Reply #30 on: April 22, 2014, 05:17:48 PM »

Update time again!

I've been reworking my collision code to take into account object velocity, and I fixed up some hacks that I had in there from the beginning to get things going. I can say that I'm pretty happy with my new system. For those interesting, I'm using the swept AABB technique described here: http://www.gamedev.net/page/resources/_/technical/game-programming/swept-aabb-collision-detection-and-response-r3084.

I implemented a debug HUD the while redoing collisions (it was necessary):



The red outlines are AABBs that have passed the broadphase check and will go through the swept AABB check. Green lines are collision bodies submitted to the collision manager (at their position at the start of the frame, not end, that is why the bullet looks out of sync). Blue lines are the normals of detected collisions.

This new system allows bullets to travel very fast without going through walls!



(please click for smooth video)

My next tasks are adding a ray casting system so that enemies can tell if they are in line of sight of the player, as well as a bunch of scripting stuff so that I can make automatic doors and other fancy things like that. I also want to start working some UI in, as well as player life. There isn't any actual gameplay yet. Much to do!

The playable demo has been updated, as always, grab it here!.
Logged

kcbanner
Level 0
***


View Profile
« Reply #31 on: April 28, 2014, 07:03:16 PM »

Here is a quick update! I've extended my animation and entity system a bit more in order to allow me to define arbitrary animations (range of frames, loop frame, frame duration) on entities and then start them from lua scripts.

Here is an enemy drone with some more complicated AI that uses this system:



(click for smooth video)
Logged

kcbanner
Level 0
***


View Profile
« Reply #32 on: May 05, 2014, 07:41:57 PM »

A quick update! I've been working on a resource system, so entities can have a map of arbitrary resources. I also added the ability for the attack component to use up resources!

Playing around with this new mechanic to dim the player's light if they deplete their energy:



(click for smooth video)

Logged

kcbanner
Level 0
***


View Profile
« Reply #33 on: May 06, 2014, 08:06:47 PM »

I finally got around to adding 3D sound! I can play sounds in 3D (well, since I'm making a 2D game, its only 2D, but the point is that they are positioned in the world), and it sounds correct as the player walks around (they get quieter, louder, etc).

Check out a video with sound here: LINK

I also added a sound to indicate the player is out of energy.


I also added blinking lights (finally) along with sounds:


(click for smooth video to see the full effect, gifs = too slow!)
Logged

kcbanner
Level 0
***


View Profile
« Reply #34 on: June 17, 2014, 06:10:50 PM »

More work on 3D sound! It now actual pans correctly into each ear as you walk by or around sound sources. I've also added some sound effects for the drone when it detects the player and explodes.

Check out the video here:



Logged

GrahamOfLegend
Level 1
*



View Profile WWW
« Reply #35 on: September 15, 2014, 05:27:44 PM »

Hey kcbanner, this looks pretty dope! I can't wait to try it out. I'm glad you changed the art of the character - this new one feels unique and fits well with your environment.
Logged

kcbanner
Level 0
***


View Profile
« Reply #36 on: June 08, 2016, 06:33:16 AM »

Quote
Warning: this topic has not been posted in for at least 120 days.
Unless you're sure you want to reply, please consider starting a new topic.

I'm sure! Caracal is back!

I spent a bunch of time building and releasing another game, Super Wizard Fever. I learned a lot about the business side of releasing a mobile game on multiple platforms, and a bit of the marketing side as well (I'm not great at it!). I built it using LibGDX, which was great for getting the game done quick, but I've been itching to get back to Caracal and apply some of the stuff I've learned in the last two years since working on it.

Over the past couple weeks I've rewritten the entity system, figured out my resource manager and asset pipeline, and got it building on Windows again with Microsoft's compiler. I'll be writing more about that stuff in future posts. Hooray for dusting off an old codebase!
Logged

kcbanner
Level 0
***


View Profile
« Reply #37 on: June 20, 2016, 08:26:18 PM »

I've really been enjoying some of the other devlogs that give insight into the implementation details
of various systems in their games. Some great examples are Mages 'n' Peasants
and of course Moonman. In the spirit of this I thought I'd document my lighting system.

Setting out, I had a few primary goals:

  • Soft shadows
  • Support for lots of lights
  • Support for moving lights
  • No pre-baking of lightmaps, since levels will be procedural

So with these goals in mind I looked for existing implementations.
I found this article
which described the exact effect I was going for. The article does a great job of expalaining the steps,
but I'll outline it again here. The main addition I made was rendering multiple lights in a single pass.

It's similar to a deferred lighting approach. The main steps in the renderer are:

1. Render diffuse map
2. Render anything that occludes light to a texture
3. Render a 1D shadowmap for each light
4. Render a lightmap using the shadowmap data for each light
5. Blend the lightmap with the diffuse map of the main scene to get the final image

In pictures it looks like this:

1. Diffuse map:


These are just the colors, straight from the sprites.

2. Occluder map:


Everything that is intended to occlude light. In my case, I just pick what I've named the "foreground" layer
in my tilemap and render it to a texture.

3. Shadow map:


Each row in the shadowmap represents a light. Each pixel in the the row represents the distance that a
ray cast at the angle x / SHADOW_MAP_WIDTH * 2PI got before it hit an occluder.

Here is the fragment shader for the shadowmap. I won't include the vertex shader, because it's just my
standard sprite vertex shader that positions 256x1 pixel quad on the right row of the shadowmap texture.

Code:
#version 150

#define PI 3.14159265359
#define ALPHA_THRESHOLD 0.75
#define STEPS 256.0

uniform sampler2D occluder;

uniform mat4 projection;

// x,y is light size, z,w is occluder texture size. Both in pixels.
uniform vec4 scale;

in vec2 texcoord;

// Light position in screen space
in vec2 lightPosition;

out vec4 color0;

void main(void) {
  float distance = 1.0;

  // Convert tex coord into an angle from (0, 2PI)
  float theta = PI * (2 * texcoord.s);
  
  for (float y = 0.0; y < scale.y; y += 1.0) {

    // Each step is one pixel of light range
    float r = y / scale.y;

    // This vector ranges from 0 -> 1 in length
    vec2 coord = r * vec2(cos(theta), sin(theta));

    // Scale factor so that we sample 1 pixel of the occluder texture
    // for each pixel of the light range (each step in the for loop)
    vec2 scaleFactor = vec2(scale.x / scale.z, scale.y / scale.w);
    coord = coord * scaleFactor;
    
    // Scale into texture coordinate space (length 0 -> 0.5)
    vec2 sampleCoord = coord.xy / 2.0 + 0.5;    

    // Offset the lookup by the light's position in the occluder texture
    sampleCoord = sampleCoord + lightPosition / 2.0;

    // Sample occluder texture
    vec4 sample = texture2D(occluder, sampleCoord);
    
    // If the sample is above our threshold, check the distance
    // and see if it is shorter than the current distance    
    if (sample.a > ALPHA_THRESHOLD) {
      distance = min(distance, r);
    }
    
  }

  color0 = vec4(distance, 0.0, 0.0, 1.0);  
}

So essentially, for each pixel in the shadowmap, cast a ray and figure out where on that ray the nearest occluder is.
Write that value to the red channel of the shadowmap texture. This texture has been set up as a floating point 256x256 texture
with only a red channel (GL_R32F).

4. Light map:


Rendered here as just the alpha channel over black.

Every light rendered as a 256x256 square, sampling the shadowmap texture to find out if
a given pixel should be lit, and how much. Apply a falloff based on the distance from the
center of the light. Mix samples from neighbouring rays to get some blurriness, increasing the
blurriness the farther we are from the light. A small about of base ambient light is added to each light so the
neighbouring tiles get some illumination.

Here's the fragment shader for the lightmap. Again, not including the vertex shader here. It just positions
a 256x256 quad centered on the light location.

Code:
#version 150

#define PI 3.14159265359
#define AMBIENT 0.03
#define STEPS 256.0
#define MAX_LIGHTS 256.0

uniform sampler2D shadowmap;

in vec2 texcoord;
in float index;
in float brightness;
in vec3 color;

out vec4 color0;

void main(void) {
  
  // Convert rectangular coordinates to polar coordinates
  vec2 normal = texcoord.st * 2.0 - 1.0;
  float theta = atan(-normal.y, normal.x);
  
  float r = length(normal);
  float sampleCoord = theta / (2 * PI);

  // Increase blur amount with radius
  float blur = (1.0 / STEPS) * smoothstep(0.0, 1.0, r);

  // Compute the Y coordinate (row in the shadowmap for this light)
  float sampleY = 1.0 - index / MAX_LIGHTS - 0.5 / MAX_LIGHTS;
  
  // Sample the shadowmap
  float sum = 0.0;
  
  vec4 samples[9];
  samples[0] = texture2D(shadowmap, vec2(sampleCoord - 4.0 * blur, sampleY));
  samples[1] = texture2D(shadowmap, vec2(sampleCoord - 3.0 * blur, sampleY));
  samples[2] = texture2D(shadowmap, vec2(sampleCoord - 2.0 * blur, sampleY));
  samples[3] = texture2D(shadowmap, vec2(sampleCoord - 1.0 * blur, sampleY));
  samples[4] = texture2D(shadowmap, vec2(sampleCoord, sampleY));
  samples[5] = texture2D(shadowmap, vec2(sampleCoord + 1.0 * blur, sampleY));
  samples[6] = texture2D(shadowmap, vec2(sampleCoord + 2.0 * blur, sampleY));
  samples[7] = texture2D(shadowmap, vec2(sampleCoord + 3.0 * blur, sampleY));
  samples[8] = texture2D(shadowmap, vec2(sampleCoord + 4.0 * blur, sampleY));

  float steps[9];
  steps[0] = step(r, samples[0].r);
  steps[1] = step(r, samples[1].r);
  steps[2] = step(r, samples[2].r);
  steps[3] = step(r, samples[3].r);
  steps[4] = step(r, samples[4].r);
  steps[5] = step(r, samples[5].r);
  steps[6] = step(r, samples[6].r);
  steps[7] = step(r, samples[7].r);
  steps[8] = step(r, samples[8].r);
  
  sum += (steps[0]) * 0.05;
  sum += (steps[1]) * 0.09;
  sum += (steps[2]) * 0.12;
  sum += (steps[3]) * 0.15;
  sum += steps[4] * 0.16;
  sum += (steps[5]) * 0.15;
  sum += (steps[6]) * 0.12;
  sum += (steps[7]) * 0.09;
  sum += (steps[8]) * 0.05;
  
  // It's at least AMBIENT bright
  sum = max(sum, AMBIENT);
    
  // Radius falloff
  float alpha = sum * smoothstep(brightness, 0.0, r);
  
  color0 = vec4(color, 1.0) * vec4(vec3(1.0), alpha);
}


5. Final composite:


Blend the diffuse map with the light map. Final scene!

I really like how the lighting turned out, and I'm especially happy that I can have a boat load of lights if I want to, with no additional draw calls. The light count is only limited by the height of the shadowmap texture. I could also increase the number of light rays by increasing the width of that texture. This would improve the fidelity of the effect, but since I'm doing a pixel art style, I don't think I need more than 256.

Let me know if you'd like more detail on the draw call submission / setup side of this effect. I didn't go into too much detail there because the original article documents most of that.

As for actual devlog updates, I'm currently chucking out my custom collision system in favor of Box2D. I really liked working with it when I did Super Wizard Fever. After using it there I realized how much my own system is lacking and decided to switch over.
Logged

kcbanner
Level 0
***


View Profile
« Reply #38 on: June 21, 2016, 08:57:12 PM »

That wasn't too bad! I integrated Box2D, replacing my old collision and physics code.



Debug drawing is implemented as well. Here you can see the foot sensor on the player, which is used to detect if jumping is allowed or not.

The next step is to better compute collision volumes instead of doing it on a per-tile basis. Right now the player can actually collide in the X direction with other floor tiles while running, or collide with wall tiles in the Y direction while falling. Making the floor and walls continuous rectangles would solve that, so that's the next step.
Logged

kcbanner
Level 0
***


View Profile
« Reply #39 on: July 19, 2016, 08:04:08 PM »

I've been working on "scripting" and triggers. The air quotes are because the scripts are just simple C++ classes that receive events and react to them, I'm not using an actual scripting language. I did actually have lua integrated a while back, but I found all the work to create bindings and pass things between lua and C++ far outweighed the time it would take to just write the C++ code. Plus, then you need an entire workflow for debugging lua scripts, when you could just use existing tools and have strong types in C++.



Here is what the script for the automatic door looks like:

Code:
namespace scripts {

class DoorScript :
public BaseScriptImpl,
public Receiver<DoorScript> {
public:

DoorScript(Entity& trigger) : triggerId(trigger.id()) {};

void start(Entity& e) override {
g_EventManager.subscribe<TriggerEnterEvent>(*this);
g_EventManager.subscribe<TriggerExitEvent>(*this);
};

void update(Entity& e, FrameParams& frameParams) override {

}

void receive(const TriggerExitEvent& event) {
if (event.triggerEntityId == triggerId) {
auto sprite = mEntity.component<SpriteDriver>().get();
sprite->setTag("close", false);
mEntity.component<Physics>().get()->body->SetActive(true);

g_SoundManager.playOneShot3D(
"event:/Environment/Door Close",
mEntity.component<Transform>().get()->position);
}
}

void receive(const TriggerEnterEvent& event) {
if (event.triggerEntityId == triggerId) {
auto sprite = mEntity.component<SpriteDriver>().get();
sprite->setTag("open", false);
mEntity.component<Physics>().get()->body->SetActive(false);

g_SoundManager.playOneShot3D(
"event:/Environment/Door Open",
mEntity.component<Transform>().get()->position);
}
}

private:

Entity::Id triggerId;

};

I also reworked my collision volumes. I use box2d edge chains now instead of a bunch of 1 unit blocks. This avoids problems where you run into the side of a floor tile as you walk along it. It also reduces the number of collidable things, which should help with performance once I have much larger levels.

Another plus of the lighting system is I had to add no special case code for light passing through the door as it opens, the door sprite is just rendered to then occlusion texture and everything works as normal.
Logged

Pages: 1 [2] 3
Print
Jump to:  

Theme orange-lt created by panic