What is a pointer?
A pointer is an address to a location in memory where a variable is stored.
This variable can either contain the actual value, or yet another pointer, pointing to another location in memory.
You usually have an pointer to an object, but the next pointer is not at the location your pointer is pointing at, but several bytes later. Thats because an object in memory consists of multiple variables which are aligned in a fixed pattern.
Some memory locations are always the same, because the variables have been made "static" in the original code. Other memory locations can only be derived by following a trail of pointers.
So what does this mean?:
Code:
"Gw2.exe"+0120F464
OFFSETS:
c0
4
b0
0
190
"Gw2.exe"+0120F464 <- This is the initial memory location which points to an variable which has been declared "static".
c0 <- The first memory location contained a pointer which pointed to an object. Add this value to the pointer to get the location of the next pointer inside that object.
Repeat until all offsets have been resolved. You now know the current address of the variable and you can read/write it.
While the program is running, the address of the variable might change (because objects in the path get destroyed and regenerated), but the static variable and the path always stays the same.
How to find a pointer?
There are several ways. The easiest is to use

to find the current address of the address. This can be done by changing attributes ingame (like your current position by moving) and scanning for values of a certain data type which changed in a certain way (increased / decreased / became zero). Once you have found the actual address, use Cheatengine to find "static paths" to this address. Cheatengine will scan the memory for pointers which point at or right before the address you have found. If the pointer points to a location right in front of your address, it means that it points to the object your variable is part of. Every time Cheatengine found a pointer, it will also try to find a pointer pointing to that pointer until it finds a special type of pointer, a "static" one.
More advanced methods include the use of an disassembler where you actually analyze the code of the program. Once you know where the variable is accessed, you can simply read the offsets right from the code. You don't even need the actual sourcecode for this, all you need is the assembler which you can get from the binary. This however is sometimes a bit complicated because you need to analyze much of the code before you can make any sense of it. You might also run into runtime packers, which means that the code does not exist in the binary until executed so you won't be able to find the paths without executing the code. gw2.exe is not packed however, so this is possible with little effort.
Many advanced users use the tool "Ida Professional" in combination with a debugger of their choice in order to examine the program. It is also possible to strip the packer by extracting the actual code of the program from the memory once the packer has been executed.
Is it really that easy?
No. Remember that '"Gw2.exe"+0120F464'? The "Gw2.exe" is there for a special reason, thats because the location "0120F464" is not as static as one might hope. There is a security feature named "address space layout randomization", short ASLR which gives every process a random offset every time a process is started. This offset needs to be added to the "static" addresses in order to get the real address in memory (again, thats still simplified!). So how to get that offset?
There are two options, the first one is pattern scanning. You scan the memory for a pattern which static address you know. If you find the pattern, you can subtract the actual memory address from the static address you know, the difference it the offset for the current instance. This however is likely to break, because static patterns are rare and might change when only minor details on the binary have been changed.
Second option is to get the offset straight from the actual thread. For this purpose, you inject code into the thread which will write the address of an static variable of your choice to an address which is not affected by ASLR where it can be read by your own program.
You may even combine these two methods to develop pattern scanners which scan for certain methods in memory (in case you don't know the location of the suitable section after an update) and the inject your code into the function found with pattern matching.