How to convert Unity code to pure C#

Hi there!

I’m working on a multiplayer solution for my asynchronous auto-battler game, which is a mix of Clash of Clans and TFT. I posted a question about this on Unity discussions here.

In my game, the players don’t directly interact with the battle. Only one player initiates the battle, even if the other player is offline. To prevent cheating, I need the server to validate the match results. One battle instance will run on the client (for display purposes), while another instance will run on the server to verify the outcome.

The easiest approach seems to be hosting a Unity instance using Unity Game Hosting to simulate the battle and determine the winner. However, this is costly. A more affordable option would be to write a battle simulation in pure C# and run it using Unity’s Cloud Code service. But I’ve never done this before, and I’m not sure how to get started. I don’t need to code graphics, only the server to do the computations with logic. But like how to represent the characters’ bodies, handle their movement, or update the simulation every frame (if there is an Update) in pure C#?

I’m not a beginner, but I’m also not an expert, so I’m unsure if this is worth the effort or if I should stick with the simpler, more expensive solution.

You dont need “unity” game hosting, you can host it yourself if you can do so cheaper. You build the server and run it on your own kit. Whether you use aws, rent a server or use a pc in your house.

And what can i run here? A unity instance or only pure C#?

you build a server build, just like a client build and you run it… wherever you are able.

  1. I am not able to host it on my own device. I surely need a service to host it to their machines. Some available services are unity hosting services, aws, playfab, photon … Etc.
  2. I am not asking about this. My question is if it is doable/worth to make a battle simulation on C# and host it at Cloud code for cheaper

Think of your battle as a simple transformation of data. You have your typical sample sales database. What would be a battle in this scenario? Most closely it’s resembled by a “report”.

You want to know how product sales across categories went for a specific location in the past year? You generate a report from the available data.

You want to know how the battle outcome will be? You generate a report from that data and report the results.

The only and major difference is “time”. Your data progresses over time, so you need to divide this into turns. Turn one sees opponent A perform its attack on opponent B. Turn two has opponent B cast a heal spell. Turn three is opponent A casting a damaging aura spell affecting all opponents. Turn four opponent B casts a meteor storm (also an aura). What does an aura do? Every turn it adds additional damage to actors.

Overall, tone it down to the bare minimum and start from there. Opponents have attack values and hitpoints. Every turn someone performs the attack, deducts hitpoints. What does the server record? The attack value and the target (the hitpoint deduction can be inferred from that).

When you play it back, you apply the damage, deduct the hitpoints, and then stretch out that turn by a start and stop event, which will cause the corresponding animations and fx to occur, and the end will allow the other opponent to make a turn.

You could also make every turn run on cloud code, not the entire battle. But frankly if you implement the former, you could as well do the latter because it’ll be more efficient and cost effective. However that’s only possible if players can’t interact during a battle in a way that it might alter the outcome.

Hey again CodeSmile. As I said my game doesnt have turns everything is happening at the same time. Could you answer my last post here because I dont want to copy the message.

It still runs in discrete steps. :wink:

Update runs 60 times per second. So the question really is - do you need to run your simulation at that rate? Most commonly, no.

I assume it works something like an RTS. A 10 Hz tick rate for the game simulation is usually enough. So you get one game simulation tick every 6 frames and the 5 frames in-between is just lerping (interpolating) everything from the previous state to the current state.

And then you can just loop over your game simulation when that’s all data at the rate the CPU can compute it to churn out the next 10 to 100 seconds of play within a fraction of a second.

I know this is highly theoretical but it all boils down to implementing it the same way a physics simulation is implemented. You have a state (world, objects) and you have rules (collisions, joints or abilities, attack frequency) and when you boil it down to just numbers, you see you’re left with bools, ints, floats and the occasional float3 for a position or direction. With these building blocks alone you can start calculating damage output and hitpoints deducted. You could run this through a spreadsheet, and you may even want to do so because this brings up all sorts of design issues (OP units/skills).

However, what you can’t easily do is to simulate any bigger system like pathfinding - which means the time to reach a target may be unknown and varies because it’s influenced by other actors. If however you can make that go away, or simplify the pathfinding, you could implement your own A*. Preferably you’d run your game on a grid, like most RTS.

Yes, in reality RTS are grid-based games even if they allow free positioning of units, each unit typically occupies a discrete grid location, or multiple units share the same grid location. Technically they’re all at 24,-13 even though visually they are distributed unevenly perhaps +/- 1 unit around that location.

Okay maybe I’m just too familiar with that kind of system (and spreadsheets and databases) so it seems natural to me. :slight_smile: Can imagine it’s more like :exploding_head: for you. :wink:
Damn thing is, I can’t find any good resources on that. :frowning:

THanks for the reply!

Here are the issues I’m running into according to your previous comment (some aren’t directly related to C#):

  1. Simulating Tick Frequency in C#: I’m not sure how to simulate the tick frequency you mentioned in C#. I haven’t been able to find much on this, and it’s my biggest issue right now.
  2. Code Accuracy and Determinism: I’m having some trouble with code accuracy and determinism. Unity doesn’t guarantee the order in which Update functions are called for different objects. So, if two units both have 1 HP, whichever unit’s Update runs first will survive, which leads to non-deterministic outcomes. To get 100% determinism, I’d need to force an execution order. But what would be the fairest approach—process all Player 1’s units first, then Player 2’s? Or alternate between players? I might need to create an array of characters to control the execution order and maybe handle that with cloud code. Since C# enforces a specific order, I’d need Unity to stick to that order as well. I came across more info on this here.
  3. Position Issues: I don’t use any pathfinding, luckily. But my characters move freely after the initial setup, which makes me worry about floating-point precision. Unity might calculate a position as x = 4.6784, while C# could compute x = 4.67849—even tiny differences like this could break determinism.

There is no frequency. It’s just a loop.

So this:

void Update()
{
    // your game tick code here
}

Becomes this:

for (var i = 0; i < gameTicksToSimulate; i++)
{
    simulation.Update();
}

You run as many simulation updates as you need to.

If you use Time.deltaTime anywhere - remove that. Delta time does not belong in a pure C# simulation. It’s only for visuals ie lerping and so on.

If units move at speeds multiplied by deltaTime, you’d have to change that to discrete movement speeds. Imagine you’re moving the code to FixedUpdate if that helps. You can assume a fixed deltaTime.

That’s a design decision. In most games one player is slightly favored over the other, such as chess (white begins). To make this fair chess players switch sides after every match and play a “best of 5” or something. So alternating would be the most fair approach. Some card, board and turn-based games also use the concept of “initiative” which can determine on every situation involving two or more units which unit goes first. For instance a Ninja character may have a high initiative and can thus often strike the first blow, same for an actor that is currently invisible.

The other thing is: don’t let each GameObject run its own Update(). Instead you’d have a global component that calls “DoYourUpdate” on these components so that you get control over the exec order.

That is why you only visually move them freely. Internally, in the simulation, they can only occupy discrete integer coordinates. Here this might be the int coordinate 9 which divided by two gives you a real coordinate of 4.5 (eg this would be the center of a 1-unit sized grid field occupying the 4 to 5 units range) and the .1234 deviation from that is your visual offset to make things look as if the units can move freely.

Ok I got everything except the last point. My characters have a particular range so that they can strike if only an opponent is in that range. In other words they move to the closest character and stop when they are in range. The problem is that my characters have float ranges and the actual decimal matter. Since the characters are small and close before the combat the ranges can’t be only integers. So I need to scale the dimensions so I can use integers. The numbers are not absolute it’s the relative scale that matters. So if I have a range of 2.5 I can make all dimensions * 10 so my range will become 25 so it can sit on an integer tile. And for all the calculations and the square roots etc I will use round up to int in both unity and C#. Is there a Chance they produce different roundings of the numbers after some calculations?

Forgot to add. I can remove Time.delta from the movement thats easy. But how can i remove if if i have a timer for when to attack which is determined by attack speed. How the attack 2 times a second for example or (1.4 times a second) can be converted to ticks in our C# Update for Cloud Code? Or even how long a buff would last? All time related events in that matter.

Since the number of update executions per second differ per system due to frame rate(we can’t guarantee to be constant) How can I keep track of “time” in C# without Time.delta time. Let’s say at 60fps for a 2 second interval the update will execute 120 times. But if the frame rate was 59 it would execute 118 times. The timedelta time includes the time it took to render the frame and compensates for it. Since our C# update is a faster repeating unity update how can we keep time without time delta

You don’t!
Never run the game simulation at the framerate. It ought to be on a fixed tick, ie FixedUpdate. Practically all games worth their money keep the framerate / frametime (delta time) out of the game simulation loop. You only need the delta time to interpolate animations and movement.

Is something in C# able to keep a fixed time? Like a time class or something that can work on cloud code?

Can something like this keep a fixed time?

Also what operating system is cloud code run on? I know is C# .NET 9 but one class above has windows in its name.

Yes, quite easily … :wink:

public const float FixedDeltaTime = 0.02f;

But this is just a variable, how it can keep time. Can you see the 2 classes I mentioned above?

Don’t start integrating external classes: you will lose control of when your little black box simulator runs, and if you pause the game now you will need all kinds of extra code to make the timer inside pause as well.

Design your API so that you tell your simulator “this much time has passed.”

Tracking whether it is time to do an update simply becomes adding the passed time to a float and when it exceeds your update interval, do the update and subtract the interval from the accumulator.

Code in your simulator might be:

private float timeAccumulator;
private const float updateInterval = 0.1f;  // 10 frames per second

private void InternalUpdate()
{
  Debug.Log("InternalUpdate(); happened.");
  // take "one step" whatever that means to your simulator
}

public void MySimulatorUpdate( float deltaTime)
{
  timeAccumulator += deltaTime;
  if (timeAccumulator < updateInterval) return;

  timeAccumulator -= updateInterval;

  InternalUpdate();
}

To use it from a MonoBehaviour, just call

MySimulatorInstance.MySimulatorUpdate( Time.deltaTime);

When you decide to offload it to a web service or another thread somewhere, use whatever mechanism is appropriate for determining time passed and call the same entrypoint.

That’s it. That’s the basis of a time-based update loop. That’s all you need for this stuff. Don’t complicate what is going to be the most trivial tiniest part of your giant task of writing a game simulator module.

So the float delta time will be sent from the client to cloudcode? Isn’t this defeats the purpose of server authoritative. I want the server to run the simulation much faster than the real battle takes place in the client.
Disclaimer. I am using cloudcode which is essentially serverless functions written in C#