|
Pirates! is the most iconic example and it does have a certain amount of fruitless circling, but it did have some countermeasures: -Winds randomly changed direction and strength, which could make continued circling difficult or impossible -Enemy ships usually had different capabilities and objectives, so true mirror matches were rare and usually one ship would not be able to keep up with the other -Broadsides were pretty devastating, and there was a lot of incentive to finish with boarding actions, so cannon duels were more of a brief softening up phase
|
# ? Apr 14, 2024 22:07 |
|
|
# ? Jun 10, 2024 13:18 |
|
Yeah that’s an interesting design challenge and while it’s nothing to do with my current game it is an idea I’ve had for fooling around with a Star Trek like thing. (Star Trek Online has a number of gameplay issues and I wonder if I could render something like its ship combat but in 2d and flowing better.) In the meantime I’m working on computers- specifically as a thing in an espionage roguelike. Mechanically they can do two things- give XP rewards for accessing secret files, and let the player do things in the level. Reveal parts of the map, reveal traps, set off traps where enemies are, etc. (This may be tricky since normally the enemies don’t exist until the player sees them because I don’t want to have to resolve turns for 20+ units all the time in every level.) Of course beyond all this I’m working on UI and trying to work out basic things like incrementing text that I know I’ve done before but not here.
|
# ? Apr 14, 2024 23:38 |
|
Also at some point I’m going to need to be able to have one of those scrolling message feeds like most RPGs and MMOs do.
|
# ? Apr 14, 2024 23:41 |
|
xgalaxy posted:Are there any good examples of 2D games with naval combat in the golden age of piracy? Puzzle Pirates does turn-based ship-to-ship combat. You lock in 4 actions, as well as when you fire your cannons, so e.g. a turn might be "go forwards; turn to the left and fire a broadside on the left side of the ship; don't move and fire another broadside on the left side; turn to the right". It's all about anticipating what your opponent is likely to do, and steering your ship such that you can hit that point and hopefully not get hit yourself. The board has winds that act like conveyor belts, and whirlpools that both move and turn any ship that ends on them; there's also rocks that act as walls. Also, your "turn left/right" actions are actually "move forward, turn 90 degrees, and move forward again" (i.e. they move diagonally forward and turn), so precise movement can be difficult sometimes. Which constrains your options and makes you easier to predict! The other option of course is to throw theming away and add a bunch of wacky poo poo to your game. Let ships jump and strafe, give them weapons that freeze the ocean, create big explosions, that kind of thing. Basically what Waves of Steel did but set 200-300 years earlier
|
# ? Apr 14, 2024 23:59 |
|
I'm toying around with Godot and GScript and I'm having a bit of trouble finding what the preferred procedure is for storing and importing basic game data. Something like how a game might have a bunch of different swords (bronze, iron, steel, mithril, crystal, etc) that have different attack values and prices. I know that many games store this data in human readable files that can be readily edited to make mods or just tweak values. In Godot, what would be the best way to do this? I know some games use scripting languages like Lua, and that C# has support for all kinds of stuff including XML. I'd like to stick with GScript since it seems like it's worth learning for ease of use with Godot. I saw that there is a "settings file" import but I'm not sure if that's suited to this purpose.
|
# ? Apr 16, 2024 05:33 |
|
JerikTelorian posted:I'm toying around with Godot and GScript and I'm having a bit of trouble finding what the preferred procedure is for storing and importing basic game data. Something like how a game might have a bunch of different swords (bronze, iron, steel, mithril, crystal, etc) that have different attack values and prices. I know that many games store this data in human readable files that can be readily edited to make mods or just tweak values. There's no built-in data table view like Unreal Editor would have, but there is a Godot plugin that adds that functionality. I've been meaning to grab it since I'm going to be needing to use something like this real soon.
|
# ? Apr 16, 2024 05:46 |
|
JerikTelorian posted:In Godot, what would be the best way to do this? I know some games use scripting languages like Lua, and that C# has support for all kinds of stuff including XML. I'd like to stick with GScript since it seems like it's worth learning for ease of use with Godot. I saw that there is a "settings file" import but I'm not sure if that's suited to this purpose. From my understanding, the usual way to do this in Godot is to have your data structure as a class that extends the Resource class. Then, you use an instance of that class as normal in your game, change the data within, etc. and then save/load the data to file with the ResourceSaver and ResourceLoader singletons. As for actually editing the resource files with a human-readable editor? You'll need to use a plugin like the one linked in a previous post, if you want to edit the resource in the Godot editor. Alternatively, you can use file formats like CSV or JSON, as the File class has methods for parsing lines from CSV or parsing JSON files into Dictionary variables. I think there are some export shenanigans that you have to be mindful of when using CSV and JSON files, so be sure to read the docs.
|
# ? Apr 16, 2024 06:07 |
|
Dieting Hippo posted:There's no built-in data table view like Unreal Editor would have, but there is a Godot plugin that adds that functionality. I've been meaning to grab it since I'm going to be needing to use something like this real soon.
|
# ? Apr 16, 2024 06:18 |
|
anatomi posted:Didn't know about that plugin. Very useful. Thanks.
|
# ? Apr 16, 2024 06:21 |
|
Speaking of Godot, I'm toying around with a roguelike in one. Got in some numpad-based movement and spent a bit of time on terrain generation. I finally got it to the point where it looks like terrain! I'll have to swap out the Kenney tileset for my own down the road, but for just getting things working now it's a godsend. https://i.imgur.com/otu5oil.mp4 The current room ID number is displayed below the player, -2 means it's a "tunnel" between two rooms. Also, the box is the "in" stairs and the bars are the "out" stairs. My current steps for generating terrain: - Generate individual rooms based on a randomized Perlin noise. This makes a bunch of organic-looking holes of walkable terrain in the map, but none of them are connected. - Find each individual room and the cells in them, grouping the cells by room. - Get the shortest distance from every cell in each room, to the closest cell in each other room. This list gets sorted by distance between room cells from the smallest distance to the largest. - Tunnel out paths between rooms in the Wall layer starting with the shortest distance, going to longest distance between their closest cells. Right before each tunneling a pathfinder check is performed, and if the two cells already have a connection through walkable terrain I skip the wall tunneling. This gives nice winding paths where each spot of the level can be explored by walking, but never usually a straight shot since the paths will be winding. If I want I can then go back to my list of room connections I skipped and insert a few more tunnels for extra path variety.
|
# ? Apr 16, 2024 08:14 |
|
I normally serialize to/from JSON in Godot. There aren't automatic methods for determining what gets serialized because there's no one-size-fits-all assumption about what needs to be serialized, but it's not too hard to write your own classes around this that tailor it to your game logic. For instance I like to write a Singleton class which can be requested to serialize some given object. It stores the path to the scene followed by one of either two things: 1) If the object has a _serialize_by_keys() method, act upon that. 2) If not, pluck out all script-defined variables and use them. _serialize_by_keys() just returns the names of all variables I explicitly want serialized. So in practice I wrote the one clever class and then I can just tag the variable names I care about on any given class. It gets a little more complicate as you drill down but it's not an unsurmountable task (and I enjoy that type of computer science).
|
# ? Apr 16, 2024 15:17 |
|
I've been working on adding a "resumable" ship spawner to Waves of Steel. Ships in the game are complicated objects, with a lot of sub-components, and they create a lot of lag when they're created. The main game handles this by pre-loading everything that each individual mission needs, and just activating the ships when they need to be spawned in, but I want to work on a "survivors"-type game mode, where new ships are continually spawning in, and I want to avoid lag. So basically I just took the core ship assembly process (where the ship parts are instantiated, which is by far the laggiest part) and moved it from a synchronous function to a generator, sprinkling in a "yield return null" after each major step. Then the existing synchronous caller goes from code like var ship = SpawnShip(...) to GameObject ship; foreach (var step in ResumableSpawnShip(...)) { ship = step; } where ResumableSpawnShip's return type is IEnumerable<GameObject> This seems to work fine, with one single solitary exception. Every ship has two copies of its hull made for graphical purposes. One helps tell the ocean where the hull is, so that the ocean shader doesn't protrude above the deck's Y position. The other creates a "shadow" in the water underneath the ship, to help sell that the ocean is semitransparent instead of opaque. They both work by being exactly the same shape as the ship's hull, and in exactly the same place. ...except that as of this most recent change, they aren't in the same place. They're slightly offset, instead, both positionally and rotationally. This causes visual oddities in-game. What's especially weird about this is that nothing in the codebase explicitly cares about these objects. The magic happens via putting one on a special layer, and using a special material for the other. Nothing should be adjusting their transforms, they have no colliders or rigidbodies, no references to them are stored and I'm sure not using GameObject.Find to dig them up. The only components on them are the transform, a mesh renderer, and a mesh filter. I went so far as to sprinkle logging throughout every single Start and Awake call in my code, tracking the position of one of the stencil objects. From this I have learned that the object's transform spontaneously changes somewhere between the end of ShipDeathEffect.Start() and the beginning of CrowdControlShim.Start(). That isn't terribly useful though. ShipDeathEffect.Start() is part of the ship in question, so basically by the time the ship is done spawning, it thinks the stencil is in the right location. CrowdControlShim is directly part of the scene, so its Start runs sometime during scene initialization as a matter of course. Naturally, there's no way to set a trap on the transform of an object, to figure out what could be changing it. For the record, this is the code that sets up the stencil objects: code:
|
# ? Apr 18, 2024 00:38 |
|
Do they have a parent GameObject that somehow changed its transform?
|
# ? Apr 18, 2024 07:29 |
|
So your ship generation is now instead of happening in a single frame, happening across multiple frames. You're doing "ship = step;" in each step, and presumably the call to ResumableSpawnShip is passing in "ship" (which changes in each frame). Then when you then call AttachWaterStencil, you pass in "GameObject ship". I'm assuming that ResumableSpawnShip is generating a new GameObject in the first place from each call, otherwise I'm not clear why you're assigning "ship" over and over. Is the "ship" you pass into AttachWaterStencil the initial GameObject, an intermediate one, or the final one? Because between every "frame" the game object will have moved. Is there another yield return null after the final one? So that the "ship" you've saved is actually one frame behind? Basically, it sounds almost like during AttachWaterStencil "ship.transform" is one frame behind or in front of where you expect. Hard to say more without knowing more about what all these bits are doing (like asset.CopyShip(), and ResumableSpawnShip()). e: Are you meant to have AttachWaterStencil be in LateUpdate after the entire creation is done; in case the order of processing is different sometimes and it's only obvious now that the creation happens across multiple frames. Red Mike fucked around with this message at 11:10 on Apr 18, 2024 |
# ? Apr 18, 2024 11:07 |
|
I can't see anything wrong in your script, although there may be something affecting the transform of one of them in either DieWithShip or ShipStencilTracker. Definitely keep looking through your ship assembly coroutine to make sure it matches the behaviour of the original function. Since the work is now being split across multiple frames, the order some functions are called in might now affect the result. Maybe try setting the parent transform in the Instantiate() function rather than afterwards with SetParent(). I assume your hierarchy looks something like this: pre:ship -> ocean cutout stencil -> water shadow stencil pre:ship -> hull -> ocean cutout stencil -> water shadow stencil Also, check if your version of Unity supports the InstantiateAsync() function. It's very useful for spawning objects at runtime without blocking the main thread and causing stutters, although it has to be done in a Coroutine or UniTask.
|
# ? Apr 18, 2024 11:11 |
|
TW0 posted:I can't see anything wrong in your script, although there may be something affecting the transform of one of them in either DieWithShip or ShipStencilTracker. you could always throw it in a Task, but it likely needs to be on the UnitySynchronizationContext anyway
|
# ? Apr 18, 2024 12:34 |
|
EDIT: fixed it. Merely destroying a rigidbody isn't enough! You have to set it to kinematic, otherwise it'll be active for the rest of the frame for some goddamn reason Maybe this is fixed in later Unity versions (I'm on 2019.4), but jesus christ.anatomi posted:Do they have a parent GameObject that somehow changed its transform? I've verified that its transform is properly "zeroed out" throughout the spawn process, but at some point later on (in the same frame, but later, and not in any Start/Awake code that I can detect), it changes, and I can't figure out why. The nature of the change (a slight displacement and rotation) would be consistent with the ship itself responding to being floated by the ocean, and that response somehow not propagating to the stencil objects. I just can't figure out why that would be. None of the rest of the game cares about these objects or knows that they exist. EDIT: OK, that "would be consistent with" line I wrote got me thinking, and I dug up the code that the ship physics system uses to track the orcean, which has now been modified: code:
This code runs on the root object of the ship, which the stencil is childed to. So the force is being applied to the ship, but not to the stencil object, causing said object to drift relative to the parent. ...is this a case of Destroy not taking effect immediately, maybe? There's code in the water stencil spawn function to remove rigidbodies and colliders, but of course, but of course that's not an immediate action... Red Mike posted:So your ship generation is now instead of happening in a single frame, happening across multiple frames. As for why I'm doing "ship = step" each step, that's the simplest way I could figure out to have a generator function actually return something. It only ever creates a single objects, which is the last object it `yield return`s. You aren't allowed to use out vars in generators, otherwise I'd do that. TooMuchAbstraction fucked around with this message at 15:46 on Apr 18, 2024 |
# ? Apr 18, 2024 15:16 |
|
TooMuchAbstraction posted:As for why I'm doing "ship = step" each step, that's the simplest way I could figure out to have a generator function actually return something. It only ever creates a single objects, which is the last object it `yield return`s. You aren't allowed to use out vars in generators, otherwise I'd do that. Ah I think I get what you're saying, you basically are creating the object at the 'first step' and then the subsequent steps aren't changing it (but need a reference to it)? If so, what I tend to do is create the game object itself (a wrapper or whatever) outside that method, and basically have the 'step' functions just accept that already-existing object. That way they never have to return anything, only modify what was passed in (which is initially blank). This also then lets you run the step functions on a pre-populated object in a different scenario. It also means you can't accidentally do `ship = X' within a step and have it somehow overwrite everything the previous steps did. TooMuchAbstraction posted:EDIT: fixed it. Merely destroying a rigidbody isn't enough! You have to set it to kinematic, otherwise it'll be active for the rest of the frame for some goddamn reason Maybe this is fixed in later Unity versions (I'm on 2019.4), but jesus christ. Ahh, welcome to Unity's crazy Destroy internal logic. Destroying the actual objects never takes effect before the end of the Update loop (but does take effect before LateUpdate, I believe), but removal of components from an object (when you destroy a component) sometimes takes effect and sometimes doesn't. DestroyImmediate doesn't suffer from this (but obviously has its own set of weirdness). Also setting it to kinematic won't for example stop it triggering kinematic collision events or whatever. If I'm understanding right, what's happening is basically: within an Update loop you're making a copy of the ship and all its components, then removing the rigidbodies/etc from the copy, but somehow the Update for another object is picking up the new object before the rigidbody removal? If so, you can avoid that by setting the ship object inactive, making the copy, then making the ship active again; assuming you're not relying on OnEnable/etc too much. The copy starts off inactive, which should stop it being picked up by Update for the other components.
|
# ? Apr 18, 2024 17:20 |
|
Red Mike posted:If I'm understanding right, what's happening is basically: within an Update loop you're making a copy of the ship and all its components, then removing the rigidbodies/etc from the copy, but somehow the Update for another object is picking up the new object before the rigidbody removal? 1. Create ship with all its components 2. Clone specifically the ship's hull, which is just a mesh/collider/rigidbody combination 3. Child the clone to the ship (which means we now have a rigidbody that's a child of another rigidbody) 4. Destroy the rigidbody and colliders 5. Physics applies forces to the ship, but not to the clone because it apparently still has its own rigidbody 6. Rigidbody and colliders are actually removed I dunno why the restructuring of my code caused this to happen, since both before and after the change, the entire ship is getting created and the Destroy calls are happening before physics gets involved.
|
# ? Apr 18, 2024 17:51 |
|
It's entirely possible that it was always broken, but the Update() calls happened to run in the right order such that it didn't matter.TooMuchAbstraction posted:1. Create ship with all its components Before your changes, #5 might have been running before #1-4 just because its Update() call happened to run first. The order is deterministic within a build but effectively random and things can influence it unexpectedly. Once #5 started running after #1-4, the fact that the rigidbody/colliders never got removed suddenly mattered. This usually tends to happen with transforms particularly because of how the transforms basically update as-the-updates-happen, so you end up with weirdness like cameras pointing at old/new locations unexpectedly. This is why a lot of smaller projects don't tend to rely on the built-in Update() method and instead have their own manager singleton object that will within its own Update order and call the real Update from every object in the scene. Which is overkill and hard to do in a performant way, but avoids these weird issues.
|
# ? Apr 18, 2024 18:23 |
|
sometimes u just gotta return https://i.imgur.com/JrxDzkF.mp4
|
# ? Apr 21, 2024 02:20 |
|
A friggin year after I started working on the current iteration of my game in earnest, I finally have the animation window showing the game looking like a weapon shop.
|
# ? Apr 21, 2024 03:41 |
|
Speaking of revisiting old things, I'm circling back to my best jam game (for the third(?) time now) but trying to do it in actual-3D: https://twitter.com/StrangeHill2DO/status/1781913670253175176 Can anyone explain how to translate a Node3D (3D Object)'s rotation into 2D (for the sprite)? global_rotation.z is clearly not quite right.
|
# ? Apr 21, 2024 06:14 |
|
I think you'd do something likepre:var fw : Vector3 = -global_basis.z var fw2d : Vector2 = Vector2(fw.x, fw.z).normalized() # if you want the rotation: var rot : float = fw2d.angle()
|
# ? Apr 21, 2024 06:37 |
|
Your Computer posted:sometimes u just gotta return digging the Spectrum vibes
|
# ? Apr 21, 2024 08:11 |
|
Been working on my games a little again this month. I keep seeing small updates I made to both of these in 2022 and like very little after, it's been such a weird like 18 months! New guy for the Zelder game https://x.com/Shoehead_art/status/1774450445995438404 And then a nasty surprise for the shooter https://x.com/Shoehead_art/status/1782041295877263622 I also fixed a heap of my lovely dumb bugs. I've a huge area for the shooter in greyboxed planning done, and heaps of little things for the zelda game. I really should stop splitting my attention like this but whatever Shoehead fucked around with this message at 14:57 on Apr 21, 2024 |
# ? Apr 21, 2024 14:54 |
|
paging TWD to the thread https://twitter.com/maxbittker/status/1782645521888247956
|
# ? Apr 23, 2024 09:33 |
|
Did some renovating on Goblin Camp's buildings menu. Now if you click a notification that references a building it'll autofill the search bar, I made the search fuzzy so it'll match even if you typo a bit _and_ buildings that don't match the filter are visible but grayed out. Feels like it makes it way easier to find the building you want to build now, and should keep working even as the number of buildings keeps growing. https://i.imgur.com/WIV4ScR.mp4 [edit: fixed the imgur link] Aryoc fucked around with this message at 12:25 on Apr 23, 2024 |
# ? Apr 23, 2024 12:23 |
|
Those were never my style games but man is that doing a good job of capturing those style games. Givin' me a pang of nostalgia for something I didn't really play.KRILLIN IN THE NAME posted:paging TWD to the thread They brute forced their way into a million dollars and I am here for it.
|
# ? Apr 23, 2024 15:38 |
|
step one: make game step two: never optimize step three: seriously, never optimize PROFIT!!
|
# ? Apr 23, 2024 15:46 |
unironically true. i cant argue with the results
|
|
# ? Apr 23, 2024 19:50 |
|
To optimize Balatro would have been premature.
|
# ? Apr 23, 2024 20:20 |
|
you don't need to optimize until you have to
|
# ? Apr 23, 2024 20:33 |
|
If it gets you over the finish line, it's good enough. But it's prudent to prepare for the likelihood that it's going to be more complicated than you thought.
|
# ? Apr 23, 2024 23:30 |
|
At some level, this complexity needs to be encoded somewhere. And sure, maybe you could have a big data table that encodes it instead, and load a CSV or JSON file or something...but depending on how many different kinds of behaviors you need to represent, that may not actually save you much.
|
# ? Apr 24, 2024 00:04 |
|
Also it’s Lua, which has been deceptively efficient for decades. Just hand it whatever poorly thought out half assed (but functional) bullshit you want to do and it’ll fly with it. Semi-famous example: Based the remastered version’s commentary, Grim Fandango had a bug where the live, dynamic, off-screen cat races they set up for one part of the game accidentally never switched off. They never noticed because it never caused any problems or performance issues. Just kept racing, frame after frame, eternally, until you hit the end of the credits and started a new game. And this was back in 1998, in a game that was kind of considered to be pushing the envelope for its time.
|
# ? Apr 24, 2024 00:32 |
|
If I had to choose between a thousand row spreadsheet and a thousand if statements, I'd probably choose the if statements. For a game like Balatro where you got a lot of entities with a lot of custom behaviors, trying to encode that on a spreadsheet sounds like more work than making the game. At least an if statement lets you customize on a per case basis. Added bonus it runs your game!
|
# ? Apr 24, 2024 00:43 |
|
IIRC Lua doesnt' have a match statement or switch statement built in, right?
|
# ? Apr 24, 2024 02:39 |
|
A common way to do that would be to use polymorphism or some other dynamic dispatch - just have a separate Behaviour object for each joker and put whatever code you want in an onScore function. But it feels like a ton of boilerplate to write it that way when you're starting out with only a half-dozen cases, it's often easier to just start with something that works well at that small scale and then keep growing it.
|
# ? Apr 24, 2024 02:46 |
|
|
# ? Jun 10, 2024 13:18 |
Tunicate posted:IIRC Lua doesnt' have a match statement or switch statement built in, right? afaik
|
|
# ? Apr 24, 2024 02:46 |