// ----------------------------------------------------------------------------------------------------------------- public CTile.EDirection SetPlayerDestination(CTile cur_tile, CTile.EDirection dir) { CTile.EDirection dir_next = CTile.DIR_EX[(int)dir].m_next; CTile.EDirection dir_next_next = CTile.DIR_EX[(int)dir_next].m_next; CTile.EDirection dir_prev = CTile.DIR_EX[(int)dir].m_prev; CTile.EDirection dir_prev_prev = CTile.DIR_EX[(int)dir_prev].m_prev; CTile.EDirection dir_back = CTile.DIR_EX[(int)dir].m_opposite; CTile.EDirection[] dir_selection_order = { dir, dir_next, dir_prev, dir_next_next, dir_prev_prev, dir_back }; CTile.EDirection final_dir = dir; foreach (CTile.EDirection d in dir_selection_order) { m_dest_tile = cur_tile.GetNeighbor(d); if (m_dest_tile != null && !m_level_tags.IsTagSet(m_dest_tile, (int)CTile.ETag.T_BORDER)) { final_dir = d; break; } } utils.Assert(m_dest_tile != null, "Destination tile is null"); m_direction = dir; m_player.SetDestination(m_dest_tile.GetPosition()); // utils.Log("Set player destination :: cur={0}:{1} dest={2}:{3} dir={4}", // cur_tile.m_x, cur_tile.m_z, m_dest_tile.m_x, m_dest_tile.m_z, dir); return(final_dir); }
// ----------------------------------------------------------------------------------------------------------------- public CTile GetTileByOffset(CTile t, CTile.EDirection dir, int offset) { int x = 0, z = 0; t.GetTilePosXZByOffset(dir, offset, ref x, ref z); return(GetTileByPosXZ(x, z)); }
// ------------------------------------------------------------------------------------------------------------- public CDirectionEx(float angle, EDirection opposite, EDirection next, EDirection prev) { m_opposite = opposite; m_next = next; m_prev = prev; m_angle = angle; m_pos.Set((float)Math.Cos(angle), (float)Math.Sin(angle)); }
// ----------------------------------------------------------------------------------------------------------------- public void GetTilePosXZByOffset(CTile.EDirection dir, int offset, ref int x, ref int z) { // Convert negative offset to positive by inverting direction if (offset < 0) { offset = -offset; dir = CTile.DIR_EX[(int)dir].m_opposite; } // Handle positive offset int z_parity = utils.GetParity(m_idx.y); int off_parity = utils.GetParity(offset); int z_parity_inv = ((z_parity == 0) ? 1 : 0); int half_offset_positive = (offset + z_parity) / 2; int half_offset_negative = offset / 2 + ((off_parity == 1) ? z_parity_inv : 0); if (dir == CTile.EDirection.DIR_3) // 3: right { x = m_idx.x + offset; z = m_idx.y; } else if (dir == CTile.EDirection.DIR_9) // 9: left { x = m_idx.x - offset; z = m_idx.y; } else if (dir == CTile.EDirection.DIR_1) // 1: right - up { x = m_idx.x + half_offset_positive; z = m_idx.y + offset; } else if (dir == CTile.EDirection.DIR_5) // 5: right - down { x = m_idx.x + half_offset_positive; z = m_idx.y - offset; } else if (dir == CTile.EDirection.DIR_7) // 7: left - down { x = m_idx.x - half_offset_negative; z = m_idx.y - offset; } else if (dir == CTile.EDirection.DIR_11) // 11: left - up { x = m_idx.x - half_offset_negative; z = m_idx.y + offset; } else // ???: should not happen { x = z = 0; utils.Assert(true, "Invalid direction"); } }
// ----------------------------------------------------------------------------------------------------------------- public void SetPlayerPosition(int tile_x, int tile_z, CTile.EDirection dir) { // Current tile CTile t = m_level.GetTileByPosXZ(tile_x, tile_z); utils.Assert(t != null, "No source tile :: pos={0}:{1}", tile_x, tile_z); // Initial position and rotation Vector3 pos = t.GetPosition(); m_player.transform.position = new Vector3(pos.x, pos.y + 0.5f, pos.z); m_player.transform.eulerAngles = new Vector3(0.0f, CTile.DIR_EX[(int)m_direction].m_angle, 0.0f); // Destination SetPlayerDestination(t, dir); // Player trail m_path.Reset(); }
// ----------------------------------------------------------------------------------------------------------------- private void Update() { // Turn right if (Input.GetKeyDown("right")) { m_direction = CTile.DIR_EX[(int)m_direction].m_next; // Turn left } else if (Input.GetKeyDown("left")) { m_direction = CTile.DIR_EX[(int)m_direction].m_prev; } // Current player position & underlying tile Vector3 cur_pos = m_player.transform.position; CTile cur_tile = m_level.GetTileByCoord(cur_pos.x, cur_pos.z); // Update compas m_compas.SetPosition(cur_pos); // Active tile is changed if (cur_tile != m_prev_frame_tile && m_prev_frame_tile != null) { // Update current and previously visited tiles m_prev_tile = m_prev_frame_tile; m_level_tags.SetTag(m_prev_tile, (int)CTile.ETag.T_VISITED); m_level_tags.SetTag(cur_tile, (int)CTile.ETag.T_CURRENT); // Update destination path m_level_tags.ClearTag((int)CTile.ETag.T_DEST_PATH); CTile t_glow = cur_tile; while (true) { t_glow = t_glow.GetNeighbor(m_direction); if (t_glow == null || !m_level_tags.SetTag(t_glow, (int)CTile.ETag.T_DEST_PATH)) { break; } } // Update trail - finalize border vertex between tiles Vector3 tile_border = m_prev_frame_tile.GetPosition() + (cur_tile.GetPosition() - m_prev_frame_tile.GetPosition()) / 2; tile_border.y += 0.5f; m_path.AddVertex(tile_border, true); // Active tile was not changed } else { // Update trail - update head vertex m_path.AddVertex(cur_pos, false); } // Destination tile is reached CTile.ERelPosition rel_pos = cur_tile.GetRelPosition(m_dest_tile, m_direction); if (rel_pos == CTile.ERelPosition.RP_CURRENT || rel_pos == CTile.ERelPosition.RP_BEHIND || rel_pos == CTile.ERelPosition.RP_NONE) { m_direction = SetPlayerDestination(cur_tile, m_direction); m_prev_frame_tile = cur_tile; } // Save tile of previous frame m_prev_frame_tile = cur_tile; // Eat apple if (m_level_tags.IsTagSet(cur_tile, (int)CTile.ETag.T_APPLE)) { m_level_tags.UnsetTag(cur_tile, (int)CTile.ETag.T_APPLE); m_tail.AddSegment(); m_config.score++; } // Create new apple if (m_level_tags.GetTagCnt((int)CTile.ETag.T_APPLE) == 0) { CTile t = CreateApple(); m_compas.SetDestination(t.GetPosition()); } // Apply level changeset Hashtable changeset = m_level_tags.GetChangeset(); if (changeset.Count > 0) { // Update all tiles in changeset foreach (DictionaryEntry entry in changeset) { CTile tile = (CTile)entry.Key; UpdateLevelMaterial(tile); UpdateLevelGameplay(tile); } // Clear changeset changeset.Clear(); } }
// ----------------------------------------------------------------------------------------------------------------- private void BuildTiles() { Texture2D heightmap = m_config.heightmap; float tile_gap = m_config.tile_gap_size; float tile_unit = m_config.tile_unit_size; float tw = m_config.tile_mesh.GetBounds().x; float th = m_config.tile_mesh.GetBounds().z; // Tile width m_tile_width = tw + tile_gap; m_tile_width_50 = m_tile_width * 0.5f; // Tile height m_tile_height = th + tile_gap; m_tile_height_75 = m_tile_height * 0.75f; m_tile_height_50 = m_tile_height * 0.5f; m_tile_height_25 = m_tile_height * 0.25f; // Level size m_width = heightmap.width; m_height = heightmap.height; utils.Log("Tile :: width={0:F2} height={1:F2} gap={2:F2} unit={3:F2}", tw, th, tile_gap, tile_unit); utils.Log("Map :: width={0} height={1}", heightmap.width, heightmap.height); // Init tags m_tag_owner = new CTag.COwner((int)CTile.ETag.T_MAX); m_tag_owner.InitTag((int)CTile.ETag.T_CURRENT, "current", 1, CTag.ELimitPolicy.LP_QUEUE); m_tag_owner.InitTag((int)CTile.ETag.T_VISITED, "visited", 10, CTag.ELimitPolicy.LP_QUEUE); m_tag_owner.InitTag((int)CTile.ETag.T_DEST_PATH, "dest-path", 25, CTag.ELimitPolicy.LP_DENY); m_tag_owner.InitTag((int)CTile.ETag.T_DEFAULT, "default"); m_tag_owner.InitTag((int)CTile.ETag.T_APPLE, "apple"); m_tag_owner.InitTag((int)CTile.ETag.T_BORDER, "border"); m_tag_owner.InitTag((int)CTile.ETag.T_TAIL, "tail"); // Build tiles m_tiles = new CTile[m_width, m_height]; for (int z = 0; z < m_height; z++) { for (int x = 0; x < m_width; x++) { CreateTile(x, z, heightmap); } } // Link together neightbor tiles for (int z = 0; z < m_height; z++) { for (int x = 0; x < m_width; x++) { CTile t = m_tiles[x, z]; for (int idx = (int)CTile.EDirection.DIR_1; idx < (int)CTile.EDirection.DIR_MAX; idx++) { CTile.EDirection dir = (CTile.EDirection)idx; t.SetNeighbor(dir, GetTileByOffset(t, dir, 1)); } } } // Create vertical border for (int z = 0; z < m_height; z++) { m_tag_owner.SetTag(m_tiles[0, z], (int)CTile.ETag.T_BORDER); m_tag_owner.SetTag(m_tiles[m_width - 1, z], (int)CTile.ETag.T_BORDER); } // Create horizontal border for (int x = 0; x < m_height; x++) { m_tag_owner.SetTag(m_tiles[x, 0], (int)CTile.ETag.T_BORDER); m_tag_owner.SetTag(m_tiles[x, m_height - 1], (int)CTile.ETag.T_BORDER); } }
// ----------------------------------------------------------------------------------------------------------------- public CTile.ERelPosition GetRelPosition(CTile target, CTile.EDirection dir) { // Easy case - target is reached CTile.ERelPosition rel_pos = CTile.ERelPosition.RP_NONE; if (target == this) { rel_pos = CTile.ERelPosition.RP_CURRENT; // Medium case - horizontal/vertical } else if (dir == CTile.EDirection.DIR_3) // 3: right { if (target.m_idx.y == m_idx.y) { rel_pos = (target.m_idx.x > m_idx.x) ? CTile.ERelPosition.RP_FORWARD : CTile.ERelPosition.RP_BEHIND; } } else if (dir == CTile.EDirection.DIR_9) // 9: left { if (target.m_idx.y == m_idx.y) { rel_pos = (target.m_idx.x < m_idx.x) ? CTile.ERelPosition.RP_FORWARD : CTile.ERelPosition.RP_BEHIND; } // Hard case - diagonal } else { // Diff to destination int z_diff = target.m_idx.y - m_idx.y; int x_diff = target.m_idx.x - m_idx.x; // Initial relative position is set to behind and offset is set to negative int offset = (z_diff < 0) ? z_diff : -z_diff; rel_pos = CTile.ERelPosition.RP_BEHIND; // Update relative position if (dir == CTile.EDirection.DIR_1) // 1: right - up { if (x_diff >= 0 && z_diff > 0) { rel_pos = CTile.ERelPosition.RP_FORWARD; } } else if (dir == CTile.EDirection.DIR_5) // 5: right - down { if (x_diff >= 0 && z_diff < 0) { rel_pos = CTile.ERelPosition.RP_FORWARD; } } else if (dir == CTile.EDirection.DIR_7) // 7: left - down { if (x_diff <= 0 && z_diff < 0) { rel_pos = CTile.ERelPosition.RP_FORWARD; } } else if (dir == CTile.EDirection.DIR_11) // 11: left - up { if (x_diff <= 0 && z_diff > 0) { rel_pos = CTile.ERelPosition.RP_FORWARD; } } else { utils.Assert(true, "Invalid diff"); // ???: should not happen } // If relative position was changed then offset should be inverted if (rel_pos == CTile.ERelPosition.RP_FORWARD) { offset = -offset; } // Find coordinates of correct tile with given offset int x = 0, z = 0; GetTilePosXZByOffset(dir, offset, ref x, ref z); // If correct tile is not our target tile then ignore if (x != target.m_idx.x || z != target.m_idx.y) { rel_pos = CTile.ERelPosition.RP_NONE; } } return(rel_pos); }