2 * Copyright 2019-2020 Iwo 'Outfrost' Bujkiewicz
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include "engine/asset.h"
12 #include "engine/logger.h"
13 #include "engine/render.h"
17 static const float movementSpeed = 1.5f;
18 static const float collisionRadius = 0.5f;
20 Scene* playerCharacter;
21 Scene* playerProjectedMovement;
22 static Transform screenToWorldMovementTransform;
23 static Vector worldMovementUp;
24 static Vector worldMovementDown;
25 static Vector worldMovementLeft;
26 static Vector worldMovementRight;
27 static Direction movementDirection;
29 static void movePlayer(Vector direction, float delta);
30 static Vector worldMovementDirection(float x, float y);
35 screenToWorldMovementTransform = identity();
36 rotate(&screenToWorldMovementTransform, (Vector) { 0.0f, 1.0f, 0.0f }, - TAU / 8.0f);
38 worldMovementUp = worldMovementDirection(0.0f, 1.0f);
39 worldMovementDown = worldMovementDirection(0.0f, -1.0f);
40 worldMovementLeft = worldMovementDirection(-1.0f, 0.0f);
41 worldMovementRight = worldMovementDirection(1.0f, 0.0f);
43 playerCharacter = newScene();
44 cameraAnchor = playerCharacter;
45 playerCharacter->solid = importSolid("assets/playercharacter.3ds");
47 playerProjectedMovement = newScene();
50 void spawnPlayer(Transform transform) {
51 playerCharacter->transform = transform;
52 reparentScene(playerCharacter, currentScene);
53 reparentScene(playerProjectedMovement, currentScene);
56 void updatePlayer(float delta) {
57 Vector direction = zeroVector();
58 if (movementDirection & DIRECTION_UP) {
59 direction = addVectors(direction, worldMovementUp);
61 if (movementDirection & DIRECTION_DOWN) {
62 direction = addVectors(direction, worldMovementDown);
64 if (movementDirection & DIRECTION_LEFT) {
65 direction = addVectors(direction, worldMovementLeft);
67 if (movementDirection & DIRECTION_RIGHT) {
68 direction = addVectors(direction, worldMovementRight);
70 movePlayer(direction, delta);
73 void startMovement(Direction direction) {
74 movementDirection |= direction;
77 void stopMovement(Direction direction) {
78 movementDirection &= ~direction;
81 static void movePlayer(Vector direction, float delta) {
82 direction = clampMagnitude(direction, 1.0f);
83 Vector displacement = scaleVector(direction, delta * movementSpeed);
85 playerProjectedMovement->transform = playerCharacter->transform;
87 Vector initialPosition = translationOf(playerCharacter->transform);
88 Vector position = initialPosition;
90 GridLocation location = gridLocationFromPosition(position);
91 bool enteredNewCell = true;
93 while (enteredNewCell) {
94 enteredNewCell = false;
95 Obstacle obstacle = getObstacles(location);
97 // Eliminate redundant corner checks
98 if (obstacle & OBSTACLE_XP) {
99 obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XP_ZN);
101 if (obstacle & OBSTACLE_XN) {
102 obstacle &= ~(OBSTACLE_XN_ZP | OBSTACLE_XN_ZN);
104 if (obstacle & OBSTACLE_ZP) {
105 obstacle &= ~(OBSTACLE_XP_ZP | OBSTACLE_XN_ZP);
107 if (obstacle & OBSTACLE_ZN) {
108 obstacle &= ~(OBSTACLE_XP_ZN | OBSTACLE_XN_ZN);
111 float edgeXp = cellBoundaryCoord(location.x + 1);
112 float edgeXn = cellBoundaryCoord(location.x);
113 float edgeZp = cellBoundaryCoord(location.z + 1);
114 float edgeZn = cellBoundaryCoord(location.z);
115 float distanceXp = edgeXp - position.x;
116 if (obstacle & OBSTACLE_XP) distanceXp -= collisionRadius;
117 float distanceXn = edgeXn - position.x;
118 if (obstacle & OBSTACLE_XN) distanceXn += collisionRadius;
119 float distanceZp = edgeZp - position.z;
120 if (obstacle & OBSTACLE_ZP) distanceZp -= collisionRadius;
121 float distanceZn = edgeZn - position.z;
122 if (obstacle & OBSTACLE_ZN) distanceZn += collisionRadius;
124 // Check all edges for intersecting already
125 if (distanceXp < 0.0f) {
126 position.x += distanceXp;
127 displacement = growVectorNoFlip(displacement, distanceXp);
130 if (distanceXn > 0.0f) {
131 position.x += distanceXn;
132 displacement = growVectorNoFlip(displacement, - distanceXn);
135 if (distanceZp < 0.0f) {
136 position.z += distanceZp;
137 displacement = growVectorNoFlip(displacement, distanceZp);
140 if (distanceZn > 0.0f) {
141 position.z += distanceZn;
142 displacement = growVectorNoFlip(displacement, - distanceZn);
146 // Calculate direct movement limits
147 Vector displacementToLimit = displacement;
148 bool reachedXp = false;
149 bool reachedXn = false;
150 bool reachedZp = false;
151 bool reachedZn = false;
152 if (displacementToLimit.x > distanceXp) {
153 displacementToLimit = scaleVector(
154 displacementToLimit, distanceXp / displacementToLimit.x);
157 if (displacementToLimit.x < distanceXn) {
158 displacementToLimit = scaleVector(
159 displacementToLimit, distanceXn / displacementToLimit.x);
162 if (displacementToLimit.z > distanceZp) {
163 displacementToLimit = scaleVector(
164 displacementToLimit, distanceZp / displacementToLimit.z);
167 if (displacementToLimit.z < distanceZn) {
168 displacementToLimit = scaleVector(
169 displacementToLimit, distanceZn / displacementToLimit.z);
172 // This is how far we can move until we collide or leave the grid cell
173 position = addVectors(position, displacementToLimit);
174 displacement = subtractVectors(displacement, displacementToLimit);
176 distanceXp -= displacementToLimit.x;
177 distanceXn -= displacementToLimit.x;
178 distanceZp -= displacementToLimit.z;
179 distanceZn -= displacementToLimit.z;
181 // Slide along obstacles
182 if ((reachedXp && obstacle & OBSTACLE_XP)
183 || (reachedXn && obstacle & OBSTACLE_XN))
185 float dz = displacement.z;
186 if (dz > distanceZp) {
190 if (dz < distanceZn) {
195 displacement = scaleVector(displacement, 1.0f - (dz / displacement.z));
198 if ((reachedZp && obstacle & OBSTACLE_ZP)
199 || (reachedZn && obstacle & OBSTACLE_ZN))
201 float dx = displacement.x;
202 if (dx > distanceXp) {
206 if (dx < distanceXn) {
211 displacement = scaleVector(displacement, 1.0f - (dx / displacement.x));
214 // Resolve crossing cell boundaries
215 // in reverse order to direct movement limits, because
216 // we only want to cross the closest cell boundary.
217 if (reachedZn && !(obstacle & OBSTACLE_ZN)) {
218 // Enter new grid cell
220 enteredNewCell = true;
223 if (!enteredNewCell && reachedZp && !(obstacle & OBSTACLE_ZP)) {
225 enteredNewCell = true;
228 if (!enteredNewCell && reachedXn && !(obstacle & OBSTACLE_XN)) {
230 enteredNewCell = true;
233 if (!enteredNewCell && reachedXp && !(obstacle & OBSTACLE_XP)) {
235 enteredNewCell = true;
239 translate(&playerCharacter->transform, subtractVectors(position, initialPosition));
242 static Vector worldMovementDirection(float x, float y) {
243 Vector direction = (Vector) { x, 0.0f, -y };
244 direction = normalized(
245 applyTransform(screenToWorldMovementTransform, direction));