Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎯 Unreal

Just starting my game dev journey

Started by SomeGuyLearningGames Jan 14 at 3:06 AM 58 replies 5.4k views
Original Post
SomeGuyLearningGames
SomeGuyLearningGames

I wanted to have a little something to mark starting out on my journey to become a game dev. I'd been trying to learn C++ for a while, but found the process so boring. So, I decided to do something a bit more hands-on and start a course on using C++ in Unreal Engine.

I was questioning if I was meant for game dev because of how tedious and monotonous it all felt. It would have broken my heart if I weren't, because I love games so much, and this was the first time I realized what I wanted to do with programming as a career. But I was mainly going through the basics, which were things I knew backwards and forwards after years of coding in other languages, so it's not surprising that I was bored.

I finally hit a lesson on the Tick function and was shown how to move a platform. They showed how to move it in one direction, but nothing else. I wondered if I understood how it worked well enough to have the platform switch directions at a certain point, and tried implementing it. IT WORKED! I was so happy! And then I immediately checked to see if I could also get the platform to pause at each end of its path, AND THAT WORKED TOO!

I know it's going to be a really hard journey going in, but to be filled with excitement at the idea of experimenting with code for the first time in years is so validating and relieving.

I also thought I'd post the code I used, both to be able to look back at my progress after some time, as well as in hopes that maybe some more experienced people might give me tips on things I can do in the future to make this more efficient, easier to read, fewer lines, etc.

void AMovingPlatform::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	
	if (!IsPlatformStill)
	{
		if (IsMovingLeft)
		{
			TestVector.X -= 1;

			//Determine if the platform has reached the end of its path
			if (TestVector.X == -410.0f) 
			{ 
				IsMovingLeft = false; 
				IsPlatformStill = true;
			}
		}
		else
		{
			TestVector.X += 1;

			//Determine if the platform has reached the end of its path
			if (TestVector.X == 220.0f) 
			{ 
				IsMovingLeft = true;
				IsPlatformStill = true;
			}
		}

		SetActorLocation(TestVector);
	}
	else
	{
		PausePlatform += 1.0f;

		//Determine if time platform has paused is up
		if (PausePlatform == 150.0f)
		{
			PausePlatform = 0.0f;
			IsPlatformStill = false;
		}
	}
}
Alberth
Alberth

Nice start.

First a problem you should be aware of. In your example, you are lucky to use integer numbers, but in most cases comparing float values with equality (==) fails to do what you think it does. An example:

#include <cstdio>

int main() {
   float x = 1.0f;
   for (int i = 0; i < 10; i++) { x -= 0.1f; }

   printf("x == 0 does %shold\n", (x == 0.0f) ? "" : "not ");
   return 0;
}

This code takes 1.0 and subtracts 10 times 0.1. In math, the result is exactly 0.0. In a computer it is not. The above prints x == 0 does not hold . The reason for that is fundamental, a computer cannot store 0.1 exactly. It is a value near 0.1 +/- 0.00000001 internally, and the above computation will thus not give exactly 0.0 as result. For more information, search the web for “what all programmers should know about floating point”.

So never compare with equality (==), always use a range (between -0.01 and 0.01, for example, or in your case against an upper or lower limit like ≥ 220 ).

For exercises, you may want to attempt:

  • you may want to create fields in the class that store the boundaries, so they are not hard-coded in the conditions. In that way you can have 5 platforms all with different left and right boundaries by creating 5 instances of the class.
  • You move 1 each tick. You may want to change that to 10 each second. Use the ‘delta_time’ that you get to compute the next position. (And move the 10 to a field in the class, to allow different platforms to have different speeds.)
Alberth
Alberth

Apparently, code formatting is broken. The #include line should include cstdio, with triangular brackets around it.

SomeGuyLearningGames
SomeGuyLearningGames

@Alberth That's actually really great advice, and something that probably would have taken me a long time to discover. Thank you. I made the changes, recompiled, and tested.

if (IsMovingLeft)
{
	TestVector.X -= 1;

	//Determine if the platform has reached the end of its path
	if (TestVector.X < -409.99f && TestVector.X > -410.01f)
	{ 
		IsMovingLeft = false; 
		IsPlatformStill = true;
	}
}
else
{
	TestVector.X += 1;

	//Determine if the platform has reached the end of its path
	if (TestVector.X < 220.01f && TestVector.X > 219.99f) 
	{ 
		IsMovingLeft = true;
		IsPlatformStill = true;
	}
}
RmbRT
RmbRT

I would avoid floats wherever possible, because floats do not always behave consistently across platforms, and also different compiler optimisations may substitute higher precision floating point instructions that merge multiple operations into one instruction which gets calculated at a higher internal precision than if you were to compute the steps separately. Also, floats accumulate rounding errors over time and all that.

Know your problem domain and use fixed-point maths for stuff that has to be exact and reproducible across platforms (basically all game state simulation, you don't want that to start to desync between players or accumulate error over time). And then you can also use == again. For something that is visual-only and doesn't need perfect precision, floats are okay, though. Floats are convenient, but you quickly fall into a sudden trap where it stops working and then it becomes a huge pain to untangle the mess. So I recommend always thinking about your number types early on.

Additionally, linear movement is best described as a linear equation with a starting point and starting time, and a direction. That way, you also don't get framerate-dependent error accumulation (for example, updating it 60 times per second gives you 60 rounding errors per second, while applying it once per second only gets you one error per second). Framerate-independent, stable simulations often employ a fixed tick interval and do not actually use the real time for simulation, but only for knowing when to apply the new tick. And some also keep two game states, the previous and the next, and interpolate between them per frame (in case the simulation ticks less frequent than the framerate). Numerical stability and accumulated errors over time and all that are complicated and aren't immediately obvious because floats are seemingly so convenient.

There are also situations where floats can become really slow suddenly, because of edge cases in float maths (usually when values get really huge or really small). Denormalisation or what it was called. Anyway, floats aren't as simple and robust as they seem, and the convenience comes at a price. If you don't know exactly what you're doing, and you need exact results, better switch to integer-based math, IMO.

Walk with God.
SomeGuyLearningGames
SomeGuyLearningGames

@undefined Some of what you said, or rather some of the terminology, was going over my head, though that's not surprising since I was doing things that didn't often require you to think about performance or things being done over and over again (static page web development). That being said, I get the gist of what you're saying. Flat integer numbers are the way to go when you need to be precise with something like this. Thank you for the advice. It's very helpful. Someone else did suggest using ranges, though, which does make a lot of sense since it'd avoid the rounding errors.

That does bring up a question, though. In this course I'm taking, it's talking about how FVector are extremely important when dealing with coordinates and other transform properties in Unreal Engine, and the three variables they hold are all floats (float X, float Y, float Z). How do you suggest I deal with that since I'm likely going to need to keep using them? Should I go with what the other guy suggested and use ranges as opposed to ==?

frob
frob

SomeGuyLearningGames said:
How do you suggest I deal with that since I'm likely going to need to keep using them? Should I go with what the other guy suggested and use ranges as opposed to ==?

Easiest is to remember that floating point numbers are approximations. There is rounding error.

Usually you get 6 decimal digits of precision, so 123456, 1.23456, 1234560000, 0.0000000123456, the decimal point can float, but anything beyond the sixth meaningful digit is effectively numerical error. The error also accumulates over time, the more math operations you make to a number the more the error accumulates. How much error accumulates depends on the details of the math you're doing.

Any time you directly compare, such as ==, it must be EXACT. About the only time it's critical is avoiding division by zero. Unreal provides functions like FMath::IsNearlyEqual() , FMath::IsNearlyZero(), and FMath::IsWithin() to help test values to see if they are very close, accounting for rounding errors.

Even further, unreal also automatically quantizes many numbers when they're saved, loaded, stored in levels, or sent across the network. That will automatically round when there's small sizes or high precision, such as below 0.01 centimeters, roughly the width of a human hair.

Relative comparisons are generally best in this case.

For iterative times, accumulate time in a value and when you've exceeded the limit, reduce the accumulator by that step. For example, if you want to do something at a regular 16ms step and are accumulating time according to graphics frames, add the time each frame. When your accumulated time is >=16ms, run the simulation a single step and subtract 16ms, repeating until it is no longer >=16ms.. Now it doesn't matter how long a single frame is, if it's 5ms or 20ms per frame, you can have any framerate you want and the simulation still works the same for passing time.

SomeGuyLearningGames
SomeGuyLearningGames

I think the last paragraph regarding time is going over my head, but I'm guessing it might make a bit more sense once I've gotten some more experience under my belt and learned more about making games. I'm still only in the first section of the course, so I've got a ways to go. Thank you, though.

The functions you listed sound extremely useful. Thank you for that. I'll see about introducing them into my code more as I work my way through the course.

I actually tried implementing FMath::IsNearlyEqual() just now, and it worked beautifully.

frob
frob

For the last paragraph…

Let's say you have an actor that is doing whatever in the Tick function: void AMyActor::Tick(float DeltaTime)

The function received how much simulation time has passed since the last time Tick() was called. It automatically handles paused games and effects for slow motion and stuff, which makes it easier for programming.

The actor might have an amount of time to wait, in the example maybe it would be TimeToPause. Maybe it's a constant, or a blueprint saved value for designers, whatever, you want it paused that long.

Rather than a direct comparison for how long pause the platform, you might have a countdown. When pause is triggered you can store it in a class member variable:

PauseTimeRemaining = TimeToPause; // Halt the platform for this long

Or maybe a similar:

PauseTimeRemaining += TimeToPause; // Extend the pause time of the platform for this long

The in your Tick you might have something like this:

void AMyActor::Tick(float DeltaTime) {
  Super::Tick(DeltaTime);
  ... 
  if ( PauseTimeRemaining > 0) {
    // I started the Tick paused and need to reduce the amount of pause remaining. 
    
    if( PauseTimeRemaining > DeltaTime ) {
      // I am still paused. Subtract the elapsed time.
      PauseTimeRemaining -= DeltaTime;
    }
    else {
      // I am becoming unpaused
      float TimeUnpausedInTick = DeltaTime - PauseTimeRemaining; // I was partially unpaused during the Tick for this long. 
      PauseTimeRemaining = 0;
      // Consider doing a partial update with TimeUnpausedInTick as how much time has elapsed.
      // The remaining time might be zero if it was an exact match, or some other number for a partial match.
     ... 
    } 
  } 
  ...
} 

The point is that the code is not looking for an exact amount of time passing. Instead it can tolerate inexact times, and it can account for timings that don't precisely align by letting it deal with a partial tick. Maybe the tick was 16 ms, it had 4 ms left being paused, you can process 12 ms of activity.

SomeGuyLearningGames
SomeGuyLearningGames

@frob OH! I get it! Okay, so there is a lesson on what DeltaTime is and I just haven't gotten to it yet, so I had no idea what that was. Okay this makes much more sense now. To confirm, when Tick() is called, the argument it passes in is how much time has passed since the last call?

SomeGuyLearningGames
SomeGuyLearningGames

@frob Unbelievable. I got to the lesson on DeltaTime, and it immediately talks about exactly what you were saying about different frame rates and how changing the position of an actor based on when Tick() is called can cause problems. I wrote a bunch of notes to make sure I understood how it works.

int MovementSpeed = 120;
CurrentLocation.X -= (MovementSpeed * DeltaTime);

ComputerA DeltaTime = 0.00833
ComputerB DeltaTime = 0.01666

For ComputerA:
MovementSpeed (120) * DeltaTime (0.00833) = ~1 per Tick() call
Tick() calls per second (1 / 0.00833 = ~120) * ~1 = ~120 change in position/second

For ComputerB:
MovementSpeed (120) * DeltaTime (0.01666) = ~2 per Tick() call
Tick() calls per second (1 / 0.01666 = ~60) * ~2 = ~120 change in position/second

I've implemented it in my code using FMath::IsWithin() since it's not going to be as precise as before, and would likely break things using FMath::IsNearlyEqual().

void AMovingPlatform::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	FVector CurrentLocation = GetActorLocation();

	
	if (!IsPlatformStill)
	{
		if (IsMovingLeft)
		{
			CurrentLocation.X -= (MovementSpeed * DeltaTime);

			//Determine if the platform has reached the end of its path
			if (FMath::IsWithin(CurrentLocation.X, -420.0f, -410.0f))
			{
				IsMovingLeft = false; 
				IsPlatformStill = true;
			}
		}
		else
		{
			CurrentLocation.X += (MovementSpeed * DeltaTime);

			//Determine if the platform has reached the end of its path
			if (FMath::IsWithin(CurrentLocation.X, 220.0f, 230.0f))
			{ 
				IsMovingLeft = true;
				IsPlatformStill = true;
			}
		}

		SetActorLocation(CurrentLocation);
	}
	else
	{
		PausePlatform += (MovementSpeed * DeltaTime);

		//Determine if time platform has paused is up
		if (FMath::IsWithin(PausePlatform, 150.0f, 160.0f))
		{
			PausePlatform = 0.0f;
			IsPlatformStill = false;
		}
	}
}
Alberth
Alberth

SomeGuyLearningGames said:
the argument it passes in is how much time has passed since the last call?

That's what Frob said, almost literally. Also, I guessed that from the name of the parameter.

SomeGuyLearningGames
SomeGuyLearningGames

@undefined I get that it was obvious to you, but when stepping into a branch of coding that you've never touched before, even having things said plainly doesn't mean it always makes sense right away. And DeltaTime was not at all obvious to someone where time lapsed has never been something they needed to consider in their coding.

Alberth
Alberth

SomeGuyLearningGames said:
I've implemented it in my code using FMath::IsWithin() since it's not going to be as precise as before, and would likely break things using FMath::IsNearlyEqual().

I wonder why you bother with an interval (that is, compare against a lower and an upper limit).

Your code seems to move the platform between 155 and 225? So why not test for pos ≤ 155 or pos ≥ 225 ? The amount of overshoot doesn't matter I would say. A platform that ends up at, say 240 due to some freak event still needs to move in the opposite direction, wouldn't it?

JoeJ
JoeJ

Skimming the thread it seems nobody stated the obvious?

I mean there is duplicated code:

	if (!IsPlatformStill)
	{
		if (IsMovingLeft)
		{
			CurrentLocation.X -= (MovementSpeed * DeltaTime);

			//Determine if the platform has reached the end of its path
			if (FMath::IsWithin(CurrentLocation.X, -420.0f, -410.0f))
			{
				IsMovingLeft = false; 
				IsPlatformStill = true;
			}
		}
		else
		{
			CurrentLocation.X += (MovementSpeed * DeltaTime);

			//Determine if the platform has reached the end of its path
			if (FMath::IsWithin(CurrentLocation.X, 220.0f, 230.0f))
			{ 
				IsMovingLeft = true;
				IsPlatformStill = true;
			}
		}

		SetActorLocation(CurrentLocation);
	}

This could be rewritten to:

	if (!IsPlatformStill)
	{
		float sign = (IsMovingLeft ? -1.f : 1.f);
		
		CurrentLocation.X -= (sign * MovementSpeed * DeltaTime);

		// Determine if the platform has reached the end of its path
		float clipped = std::clamp (CurrentLocation.X, -420.f, 220.f); // idk the UE function for clamp, so using std as a palceholder
		if (clipped != CurrentLocation.X)
		{
			IsMovingLeft = !IsMovingLeft; 
			IsPlatformStill = true;
			// CurrentLocation.X = clipped;
 // not sure if desired
		}

		SetActorLocation(CurrentLocation);
	}

Although i have changed the logic from a given stop region on either side to a single range, which may matter or not.

We could simplify further, by giving MovementSpeed a sign instead having a redundant boolean variable IsMovingLeft:

	if (!IsPlatformStill)
	{
		CurrentLocation.X -= (MovementSpeed * DeltaTime);

		// Determine if the platform has reached the end of its path
		float clipped = std::clamp (CurrentLocation.X, -420.f, 220.f);
		if (clipped != CurrentLocation.X)
		{
			MovementSpeed *= -1.f; 
			IsPlatformStill = true;
			// CurrentLocation.X = clipped;
 // not sure if desired
		}

		SetActorLocation(CurrentLocation);
	}

So we now have both simpler and less code, and also simpler data structures by getting rid of a redundant variable.
We also have simpler behavior, which is a bit different, but usually simpler is better.

Remember: Code which is more complicated than needed is bad code. It's harder to read and harder to maintain.

EDIT:

Another option to simplify would be to get rid of IsPlatformStill by setting MovementSpeed to zero.
But then we would need to keep IsMovingLeft to define the direction of future motion, eventually.

I can't tell from a distance, but i can see there are potential ways to simplify.
And you should always spend some time on looking for such opportunities.

SomeGuyLearningGames
SomeGuyLearningGames

@JoeJ I'm pretty new to a lot of things C++ (I mostly have worked with C# and either used decimals or ints due to the nature of my work), so I hadn't realized you could shorten something like 120.0f to 120.f.

This was also one of those cases of “I can read the code, but what is the purpose of this part”, which I truthfully love coming across that and then having the “Aha!” moment of figuring it out. For me, it was the line:

MovementSpeed *= -1.f;

I understood it threw the object's direction into reverse, but as obvious as it is in hindsight, it took me a moment to realize that this replaces the need to say whether the platform is moving left or right along the X-axis once you've reached one edge of the range. Probably because I hadn't considered the idea of using negative values for movement.

Thank you for this. I'm definitely storing this code to use, considering that after I finish the course I'm taking, I'd like to create a mini-Tomb Raider style game as my first project, crushing walls and all.

You're right in trying to simplify code. I do web development and created a page with tons of data tables, and because it was one of the first things I did, it ended up being over 2000 lines of code. I got to redo it and shortened it to just over 700 lines while keeping all functionality and even adding new functionality.

I really do appreciate the help. I imagine I'll probably be reusing this code in one way or another a lot with actor movement.

By the way, I looked it up, and the UE version of std::clamp is FMath::Clamp. Convenient.

EDIT: I've realized, when trying to implement this code in mine that I've since posting, switched to using the entire FVector for velocity instead of just the X value, so I'll need to edit my code a bit more to make it work. Likely add something like a MinVector and MaxVector FVector as member variables (so things don't look too gross by having a bunch of literal numbers in the middle of my code) and then separate variables using FMath::Clamp to check the different axis. It'd be lovely if Clamp could compare FVectors as a whole.

JoeJ
JoeJ

SomeGuyLearningGames said:
It'd be lovely if Clamp could compare FVectors as a whole.

This is possible but requires some deeper knowledge on linear algebra using multiple dimensions, which you probably don't know from web dev experience.

Say we define our end points of a line:

vec3 point0 (-420, 0, 0);
vec3 point1 (220, 0, 0);

(I'm using vec3 instead FVector, as used by shading languages, because that's commonly known syntax.
And i'm too lazy to add this annoying .f everywhere, although i should.
And i'm keeping the line on the x axis only as before, but it still works if you set the other dimension to non zero values as well.)

And we have some initial position to our actor:

vec3 actorPos = vec3(300, 20, 0);

And your goal is to project this point to the closest point on the line.

Now you may use clamp 3 times, for each the x,y,z dimensions separately:

actorPos.x = clamp(actorPos.x, point0.x, point1.x);
actorPos.y = clamp(actorPos.y, point0.y, point1.y);
actorPos.z = clamp(actorPos.z, point0.z, point1.z);

But this does not project to the line. It only clips the actor inside the ‘axis aligned bounding box’ given by the line.
So it does not work and need we need better math:

vec3 line = point1 - point0;
vec3 diff = actorPos - point0;

float len = length(line); // will give us 640, the length of the whole line
vec3 dir = line / len; // gives the direction vector of the line, but with a length of one. A 'unit vector'. Which we could also calculate using dir = normalize(line)

float t = dot(dir, diff); // projects the actor to the line direction, giving a positive value if dir and diff point in the same direction, negative otherwise

t /= len; // rescale the value to factor back in the actual length of the line

vec3 projActor = point0 + line * t; // this gives us the actor projected to the infinite line. the value should be (300,0,0).

// so we solved the projection, but not yet the desired clipping

float clippedT = clamp (t, 0.f, 1.f); // which is a s simple as clampint to the (0...1) range.

vec3 clippedProjActor = point0 + line * clippedT; // now the actor can not exceed the line on either end (220,0,0), which is what we want

Be sure to understand the geometry of this example. Spend some time on it, paying attention to the dot product.
It's the most fundamental math lesson you can have, and critically important to game dev in general.

Once understood you can optimize and simplify it to this:

vec3 line = point1 - point0;
vec3 diff = actorPos - point0;

float t = dot(line, diff) / dot(line, line); 
vec3 clippedProjActor = point0 + line * clamp (t, 0.f, 1.f);

Which avoids the square root needed to calculate length of the line.

Imo, mastering this kind of math is equally important than programming skill, if not more.
It takes some time to get used to it, but it should become second nature.
Doing debug visualizations can help a lot with ‘understanding geometrically’.

Once this works, you can even use a velocity vector which is not parallel to the line, but the actor will still stick to the line as desired.

SomeGuyLearningGames
SomeGuyLearningGames

Hoooo boy. Yeah, you took it up to the next level. You're right in thinking web dev has never thrown anything involving multiple dimensions at me. I'll definitely need to spend some time going over this and internalizing everything.

I also have a course I probably need to get to eventually on 3D math. I haven't gotten too deep into understanding what goes on in there, other than knowing that some basic trigonometry and calculus can be used. Would this be in line with the types of things I might learn in there?

I also have another question, not on syntax or knowledge of coding, but more on what it takes to be a good, creative coder in the gaming industry, if you're willing to humor me. I look at some of the things people can do on here (like what you just did), and it gives such a horrible sense of imposter syndrome. Like, I can study and practice the stuff (I've already added four new functions to the documentation I'm keeping on useful functions just from this post thread alone), but how can I be as creative as they are? Are these all things you've just picked up over time and slowly worked into your normally used codebase, or do you need to be able to think creatively like this from the get-go? I've seen solutions in places that prepare you for interviews with practice coding questions that are so creative and effective, but it took me 30 minutes of looking at it and thinking about it to understand the purpose of all the parts so that I could actually use them myself.

I legitimately want to be a part of the industry, and this is the first time in years that I've gotten excited at the idea of just experimenting with different kinds of code. It's just hard to imagine reaching a level where I can come up with a solution like that on the fly.

JoeJ
JoeJ

SomeGuyLearningGames said:
I also have a course I probably need to get to eventually on 3D math. I haven't gotten too deep into understanding what goes on in there, other than knowing that some basic trigonometry and calculus can be used. Would this be in line with the types of things I might learn in there?

Well, personally i represent the guy who has learned almost nothing about math in schools, so it was a long and difficult journey to catch up, and it's still going on. So take what i say with a grain of salt.

Regarding trig, it's not that important. It's actually more important to learn how to avoid a need for it. Which is a long termed optimization topic.

Regarding calculus, it's not that important either. Surely you want to understand how basic integration of acceleration → velocity → displacement works for example, but that's still basic and trivial as long as you do not need to work on physics constraint solvers. So you won't spend too much time on calculus at first.

The most important is linear algebra. Understanding how matrices can represent 3D rotations, or simple geometric problems like projecting a point to a line or to a plane. That's the most basic stuff you need all the time, and the dot product is the key to all those examples.
Beside geometry, the other way to look at liner algebra is representing multiple linear equations in matrix form and solving them. But i would say that's not your initial and primary interest either.

Focus on geometry to answer your questions about 3D space. Points, vectors, matrices, quaternions. That kind of stuff.

SomeGuyLearningGames said:
Are these all things you've just picked up over time and slowly worked into your normally used codebase, or do you need to be able to think creatively like this from the get-go?

It takes time. Using our example of projecting a point to a line, i don't expect you fully grasp it now and in a single day, even it is indeed simple.
The process will rather be like this: You encounter this similar problem 10 times along your journey, and you will google similar questions 10 times, looking back to older code when you dealt with a similar problem 10 times, etc.
But then, after you used it again and again, it becomes second nature. You no longer need to look up older things, you can write the code from scratch from now on, now ant each time in the future you'll need it again.
At this point it also became a tool of your mental problem solving skills, which equals creativity in coding.
Your number of available tools grows with time, and solving simpler problems becomes trivial, and tackling harder problems becomes possible.

It feels like a never ending process of becoming smarter and more powerful.
But then you get old, and you realize some of your capabilities start to shrink instead growing further.
You can still compensate with experience for some time. (I'm at this point)
But well, at some point it's over. You make place for a new generation, and prepare to die. (I hope i still have enough time left to finish what i've started.)

SomeGuyLearningGames said:
but it took me 30 minutes of looking at it and thinking about it to understand the purpose of all the parts so that I could actually use them myself.

I am super slow with such things. Actually i can only focus on problems that i currently have myself. If i try to learn general math stuff from various resources, it just goes in left and out right, without any enlightenment happening in the middle.
So the only way to learn something for me is working on it.

I guess i'm not totally alone here, and it's the primary problem of education.

SomeGuyLearningGames said:
It's just hard to imagine reaching a level where I can come up with a solution like that on the fly.

You can't imagine a higher level which is still in front of you. Nobody can. Those who think they could never learn anything, and keep stuck at making X just in their imagination.

Contrary, if you realize that you don't know (yet), but you keep working, then you are on the right track.
Personally i have a good impression about your attitude and mindset.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.