Development Log
Wednesday, 4 November 2015
Wednesday, 3 December 2014
Game Engines Blog 5 - level editor
We've pretty much finished out prototype for this semester's GDW. It has a tutorial and all of our gameplay mechanics implemented.
So for our offline tool I've implemented a level editor that allows you to move around and manipulate various gameplay triggers like spawn points and to be able to save their positions to a text file.
Here's some of the code for reading the spawn positions.
core_str::String fileSource;
core_str::String fpath("/Save/Multiplayer_Spawns.txt");
core_io::Path filePath((GetAssetsPath() + fpath));
core_io::FileIO_ReadB fileRead(filePath);
fileRead.Open();
core_str::String filecontents;
fileRead.GetContents(filecontents);
const char * filecontents_char;
filecontents_char = filecontents.c_str();
int characterIncr = 0;
int posIncr = 0;
char character[50];
for (int i = 0; i < 50; i++)
{
if (filecontents_char[i] == '-' ||
filecontents_char[i] == '0' ||
filecontents_char[i] == '1' ||
filecontents_char[i] == '2' ||
filecontents_char[i] == '3' ||
filecontents_char[i] == '4' ||
filecontents_char[i] == '5' ||
filecontents_char[i] == '6' ||
filecontents_char[i] == '7' ||
filecontents_char[i] == '8' ||
filecontents_char[i] == '9')
{
character[characterIncr] = filecontents_char[i];
characterIncr++;
}
if (filecontents_char[i] == ',')
{
//convert to number
characterIncr = 0;
float number;
number = (float)atoi(character);
if (posIncr < 3)
spawn_beacon[0].pos[posIncr] = number;
else
spawn_beacon[1].pos[posIncr - 3] = number;
posIncr++;
for (int a = 0; a < 20; a++)
{
character[a] = ',';
}
}
}
fileRead.Close();
and writing
core_str::String fileSource;
core_str::String fpath("/Save/Multiplayer_Spawns.txt");
core_io::Path filePath((GetAssetsPath() + fpath));
core_io::FileIO_ReadAndWriteEmptyB fileWrite(filePath);
fileWrite.Open();
for (int x = 0; x < 2; x++)
for (int i = 0; i < 3; i++)
{
char text[32];
int num = (int)spawn_beacon[x].pos[i];
sprintf(text, "%d", num);
core_str::String line = core_str::Format(text);
fileWrite.Write(line);
fileWrite.Write(",");
}
fileWrite.Close();
Tuesday, 25 November 2014
Game Engines Blog 4 - 2LoC
This blog I'm going to be talking about my experience using the 2LoC engine as a 3rd year student game developer. This engine in no doubt gives a lot of useful tools for easily rendering simple geometry or text and is pretty organized with its entity and component system. The samples included gives a lot of useful examples to obtain code and learn some of the functionality of the engine. The project setup with cmake allows me to create multiple instances of a project with all of them sharing the same assets path saving space. But while all of these aspects are great, I feel that it comes with a lot of drawbacks. To start off, there is very limited documentation available online and not much of a community which makes it more difficult for me to resolve an issue in a short period of time, and there have been many instances where I was completely clueless as to why some things weren't functioning properly or having massive frame rate drops without knowing the underlying cause. Another example would be the random euclidean values being assigned to floats belonging to other entities when creating new separate materials completely unrelated to those objects. There are also instances where behaviors of certain entities change every time I re-run the project even though none of the code was changed. These unpredictable behaviors and being unable to understand the background operations has caused some deal of frustration at times and has often led me no choice but to look for a work around fix. Some of things I was easily able to accomplish in last year's framework takes more effort to mimic in 2Loc and may run slower as well. I've also found sending in uniforms to the shaders is more of a complex task then it needs to be. Compared to last year, I feel I have a lot more overhead when working with 2LoC in this year's workflow. But the engine is pretty sophisticated and I'm overall impressed with the modularity of its entity and component systems, but I feel that it still needs some work and optimization to remove these unpredictable behaviors and slow downs.
Sunday, 2 November 2014
Game Engines Blog 3
(In game screenshot)
These past few weeks I have been playing around with more texture backing and global illumination. And seeing how our maps will be static, this made sense for us to bake some of the lighting effects. I think the end result looks pretty nice, it gives a more realistic look to our levels.
I've added some bloom post processing effect to our game as well. This was done by creating an FBO with color and depth attachments along with a post processing shader that works on a single texture.
(In game screenshot)
Our gameplay will have a heavy focus on multiplayer, so I wanted to have a quick way to test it early. What I did was implement a familiar library called SDL_Net, which does very simple server to client communication with TCP/IP packets.
//networking
if (isOnline)
{
net->send(&netPlayer);
net->recieve(&opponent, &netPlayer);
}
I've set up a simple server that will assign IDs to each game client. The clients have 2 functions, Send and Recieve, once the server receives a packet, it will send out the packet to other clients that isn't the sender. This is a very basic form of networking and can work through LAN connection by assigning the client's with the appropriate IPv4 address.
Each packet data simply contains player data stitched into a single char array format then sent to the server application.
math_t::Vec3f pos = p->model.position;
//this packs data into a single char array (tmp)
// 1 = packet id, %d = ID, %f positionX, %f positionY, %f positionZ, %1f rotation_angle
//%d for int, %f for float, %1f for double
sprintf(tmp, "1 %d %f %f %f %lf \n", p->getID(), pos[0], pos[1], pos[2], p->rotation_angle);
int size = 0;
int len = strlen(tmp) + 1;
while (size < len)
{
size += SDLNet_TCP_Send(connection, tmp + size, len - size);
}
And that's the current state of our game. We plan to use this to polish our gameplay and test various forms of PVP game types in the near future.
Wednesday, 15 October 2014
Game Engines Blog 2
We’ve decided to change our game idea to a competitive
first person shooter. We wanted our game to have a fast paced, fun and
competitive feel, and by incorporating elements like wall running can add
variation to gameplay similar to Mirror's Edge. Another great aspect was the game's camera
movement, it gives the player a very intense feeling when maneuvering through
obstacles and looks more realistic. These are the elements I want to focus on
the most while my other team member wants to focus on the shooting.
So for this week I started to implement basic
movement, physics and model loading into the engine. We want swift movement
along with a wall running mechanic to have fast gameplay. I started off with a
basic camera class that moves in a FPS fashion by calculating the forward,
right and up vectors and moving the entity along those directions.
I have also created an object class to easily create
new obj entities in the scene in only 3 lines of code.
OBJModel player;
player.Load("Crate.obj", "crateTexture.png");
player.Init(entityMgr, cpoolMgr,
shaderPathVS, shaderPathFS);
For collision, a map class was made to extract the
vertex information of a map obj file to be used with collision checking.
bool Map::Load(core_str::String model, core_str::String texture)
{
this->Model.Load(model, texture);
gfx_med::ObjLoader::vert_cont_type model_verts = Model.Get_Vertices();
for (gfx_med::ObjLoader::vert_cont_type::iterator itr = model_verts.begin(), itrEnd = model_verts.end();
itr != itrEnd; ++itr)
{
math::types::Vector_TI<float, 3> vert =
itr->GetPosition();
mesh.push_back(math_t::Vec3f(vert[0], vert[1], vert[2]));
//also
needs verts to be transformed by the modelmatrix (NEEDS UPDATING)
}
return true;
}
Once that’s
done I’ve implemented mesh collision between each individual triangle of the
mesh and a sliding sphere.
bool camera::Collide(core_conts::Array<math_t::Vec3f> coll_verts, input_hid::keyboard_b_vptr& keyboard)
{
bool result = false;
int verts_size = coll_verts.size();
if (verts_size > 0)
{
for (int i = 0; i < verts_size; i += 3)
{
if (Collision_Tools::GetInstance()->IntersectsTriangle(this->Coll_Sphere, coll_verts[i], coll_verts[i + 1], coll_verts[i + 2]))
{
math_t::Vec3f &vertex1 = coll_verts[i];
math_t::Vec3f &vertex2 = coll_verts[i + 1];
math_t::Vec3f &vertex3 = coll_verts[i + 2];
math_t::Vec3f closestpt = Collision_Tools::GetInstance()->NearestPointOnTriangle(this->Coll_Sphere.center,
vertex1, vertex2, vertex3);
math_t::Vec3f closestpt_subtr_coll_sphere
= (closestpt - this->Coll_Sphere.center);
closestpt_subtr_coll_sphere.Normalize();
math_t::Vec3f PlaneNormal =
closestpt_subtr_coll_sphere;
float penetrationdepth = abs(this->Coll_Sphere.radius -
closestpt.Distance(this->Coll_Sphere.center));
float velocityAlongNormal =
velocity.Dot(PlaneNormal);
float dotangle =
abs(PlaneNormal.Dot(math_t::Vec3f(PlaneNormal[0], 0, PlaneNormal[2])));
if (dotangle < 0.6f)
{
this->on_ground = true;
PlaneNormal[0]
*= 0;
PlaneNormal[2]
*= 0;
}
if ((dotangle > 0.8f
&& dotangle < 1.2f) && !on_ground)
{
wall_normal =
PlaneNormal;
if (mode == 1)
{
previous_direction
= direction;
previous_direction[1]
= -0.08f;
}
if (keyboard->IsKeyDown(input_hid::KeyboardEvent::space) || wall_run_timer
> 0)
{
}
wall_run_timer
= 0.2f;
}
if (velocityAlongNormal >
-penetrationdepth)
{
this->velocity -= PlaneNormal
* (penetrationdepth + velocityAlongNormal);
//this->state.velocityAlongWall =
glm::normalize(this->velocity);
result = true;
}
}
}
}
return result;
}
This algorithm calculates the velocity of the player
against a wall.
For the
rest of the week, I made a prototype testing out the wall running mechanic. In
this example, I made a map where the player needs to wall run across to get to
the other side of the building.
This is
done by simply moving the player against the inverted normal of the wall and
moving the player in the forward velocity along the wall.
Thursday, 25 September 2014
Game Engines Blog 1
So for this
week, after getting tloc set up and running I began to look at a couple of
samples. I just wanted to get a camera setup and running with a model so I
looked specifically at the obj loader sample with the arc ball camera. At first
I wasn’t used to the amount of use of inheritance and pointers used in tloc,
but I quickly got used to the entity and component concept because I’ve been
playing around with the Unity game engine all summer and have made some
prototypes using it. I tried changing the positioning and rotation of the
camera however it wasn’t behaving properly so I decided to write my own view
matrix function which returns a 4 by 4 matrix determining where the camera will
look at. This has allowed me to focus the camera on our player object.
math_t::Mat4f GetViewMatrix(const math_t::Vec3f& eye, const
math_t::Vec3f& target)
{
math_t::Vec3f
up = math_t::Vec3f(0.0f, 1.0f, 0.0f);
math_t::Vec3f
zaxis = math_t::Vec3f(eye - target);
zaxis.Normalize();
math_t::Vec3f
up_cross_zaxis;
up_cross_zaxis.Cross(up,
zaxis);
math_t::Vec3f
xaxis = up_cross_zaxis;
xaxis.Normalize();
math_t::Vec3f
zaxis_cross_xaxis;
zaxis_cross_xaxis.Cross(zaxis,
xaxis);
math_t::Vec3f
yaxis = zaxis_cross_xaxis;
float dot_xaxis_eye = xaxis.Dot(eye);
float dot_yaxis_eye = yaxis.Dot(eye);
float dot_zaxis_eye = zaxis.Dot(eye);
math_t::Mat4f
viewMatrix(xaxis[0],yaxis[0],zaxis[0], 0,
xaxis[1],yaxis[1],zaxis[1], 0,
xaxis[2],yaxis[2],zaxis[2], 0,
-dot_xaxis_eye, -dot_yaxis_eye, -dot_zaxis_eye,
1);
return viewMatrix;
}
Next was to
rotate the player box and have the camera follow the rotation and face where
the player is facing. This was simply done by taking in the player’s rotated
angle and calculating the rotation of the camera around the player’s position.
//rorate the entity around the Y axis
void RotateEntity(const
core_cs::entity_vptr a_ent, double angle)
{
math_cs::transform_sptr
transform = a_ent->GetComponent<math_cs::Transform>();
math_cs::Transform::orientation_type
orien(transform->GetOrientation());
//rotating around Y axis test
math_t::Mat3f
RotationMatrix((float)cos(angle), 0, (float)sin(angle),
0, 1, 0,
(float)-sin(angle),
0, (float)cos(angle));
transform->SetOrientation(RotationMatrix);
}
I've also added spring physics to the camera so it adds a bit of a delay following the player.
//camera with spring physics (chase camera) for adding delay while following the player
static math_t::Vec3f camVelo(0);
math_t::Vec3f originalPos = transform->GetPosition();
math_t::Vec3f targetPos = newCamPos;
float stiffness = 0.8f;
float damping = 0.15f;
float mass = 0.005f;
math_t::Vec3f stretch = originalPos - newCamPos;
math_t::Vec3f force = -stiffness * stretch - damping * camVelo;
math_t::Vec3f acceleration = force/mass;
camVelo += acceleration * 0.001f;
newCamPos = originalPos + (camVelo * 0.001f);
newCamPos[1] = targetPos[1];
static math_t::Vec3f camVelo(0);
math_t::Vec3f originalPos = transform->GetPosition();
math_t::Vec3f targetPos = newCamPos;
float stiffness = 0.8f;
float damping = 0.15f;
float mass = 0.005f;
math_t::Vec3f stretch = originalPos - newCamPos;
math_t::Vec3f force = -stiffness * stretch - damping * camVelo;
math_t::Vec3f acceleration = force/mass;
camVelo += acceleration * 0.001f;
newCamPos = originalPos + (camVelo * 0.001f);
newCamPos[1] = targetPos[1];
Monday, 31 March 2014
UOIT Game Dev - Development Blog 10 - The art of Journey
For a long time I have been amazed and captivated by the unimaginably beautiful art style of TGC's playstation 3 title "Journey". The game features a robed figure that players control to explore different types of terrain in different climates and the purpose is to reach the peak of the mountain seen in the distance. The gameplay mechanics mainly revolve around simple platforming and collecting pieces of scarves.
It was one of the few games I have come across which art style and graphics had brought so much emotion to a player. To me it felt like a disney movie, the design that went into this game was truly beautiful.
So I wanted to look up how the rendering was done for terrain and I've come across several sources including a power point presentation from TGC.
And not surprisingly, the process takes several steps.
- Heightmaps
There are actually 3 types height maps used. A single 256x512 artist generated image was used for generating the terrain, this was done by using B-Spline interpolation to smooth out the hills during real time. I found this to be a really clever way to avoid using a massive resolution height map as it could take up a lot of space. Detail height maps were used to create the ripples in the sand, various types of tiled maps were interpolated between to give the terrain more unique details. And a 3rd type of height map was used to create sand waves.
- Diffuse Contrast
A modified version of the Lambertian model was used in the final product, the Oren-Nayar model helped bring out more contrast out of the terrain.
this is the shader code used:
this is the shader code used:
half OrenNayarDiffuse(
half3 light, half3 view, half3 norm, half roughness )
{
half VdotN = dot( view, norm );
half LdotN = dot( light, norm );
half cos_theta_i = LdotN;
half theta_r = acos( VdotN );
half theta_i = acos( cos_theta_i );
half cos_phi_diff = dot( normalize( view - norm * VdotN ),
{
half VdotN = dot( view, norm );
half LdotN = dot( light, norm );
half cos_theta_i = LdotN;
half theta_r = acos( VdotN );
half theta_i = acos( cos_theta_i );
half cos_phi_diff = dot( normalize( view - norm * VdotN ),
normalize( light -
norm * LdotN )
);
half alpha = max( theta_i, theta_r ) ;
half beta = min( theta_i, theta_r ) ;
half sigma2 = roughness * roughness;
half A = 1.0 - 0.5 * sigma2 / (sigma2 + 0.33);
half B = 0.45 * sigma2 / (sigma2 + 0.09);
return saturate( cos_theta_i ) *
half alpha = max( theta_i, theta_r ) ;
half beta = min( theta_i, theta_r ) ;
half sigma2 = roughness * roughness;
half A = 1.0 - 0.5 * sigma2 / (sigma2 + 0.33);
half B = 0.45 * sigma2 / (sigma2 + 0.09);
return saturate( cos_theta_i ) *
(A + (B * saturate( cos_phi_diff ) *
sin(alpha) * tan(beta)));
}
}
- Sharpened mip-maps
One issue with mip-mapping is that objects or terrain further in the distance begin to lose a lot of detail and have more of a smoother look. The sand in Journey needs to have more of a grainy look, so sharpened mip-maps were used to bring out the detail and texture of the sand in further distances.
Sharp mip-maps off
Sharp mip-maps on
- Specular glitter
When I first saw the game, what stood out to me the most was the glittering sand effect. Naturally, sand is comprised of rocks and minerals that reflect like sharp crystals shining specular sun light into our eyes giving that glitter effect.
Most of the sand texture was derived from noise normal maps. This was used bring out the specular detail of individual grains of sand.
- Anisotropic masking
The effect had an issue though, some parts of the terrain had an unusual concentration of glitter which gave an unnatural feel.
To solve this, a mip-mapped texture containing values based on distances from the camera filters out the inappropriate specular highlights.
And that's most of it, the rest include fluid simulation, a dust system and bloom.
This is my 10th and final post and I would like to mention that I've learned so much over the past year. Computer graphics is truly amazing and you don't need to be a mathematician to do it, games like Journey have inspired me to really dive into it. This year I didn't really have too much time as I liked on shaders, building a 3D game from scratch and incorporating all of the functionality was a challenge. However, next year we get to use a pre-built game engine and I definitely plan to give shaders a lot of attention and create a beautiful game.
Thank you for reading :)
Subscribe to:
Posts (Atom)