├── PacmanGame ├── Blinky.pde ├── Clyde.pde ├── Inky.pde ├── Node.pde ├── Pacman.pde ├── PacmanGame.pde ├── Path.pde ├── Pinky.pde ├── Tile.pde └── data │ └── map.jpg └── README.md /PacmanGame/Blinky.pde: -------------------------------------------------------------------------------- 1 | class Blinky { 2 | PVector pos = new PVector(13*16 +8, 11*16+8);//starting position 3 | PVector vel = new PVector(1, 0); 4 | Path bestPath; // the variable stores the path the ghost will be following 5 | ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position 6 | Node start;//the ghosts position as a node 7 | Node end; //the ghosts target position as a node 8 | color colour = color(255, 0, 0);//red 9 | 10 | boolean chase = true;//true if the ghost is in chase mode false if in scatter mode 11 | boolean frightened = false;//true if the ghost is in frightened mode 12 | int flashCount = 0;//in order to make the ghost flash when frightened this is a counter 13 | int chaseCount = 0;//counter for the switch between chase and scatter 14 | boolean returnHome = false;//if eaten return home 15 | boolean deadForABit = false;//after the ghost returns home it disappears for a bit 16 | int deadCount = 0; 17 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 18 | //constructor 19 | Blinky() { 20 | setPath(); 21 | } 22 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 23 | 24 | void show() { 25 | //increments counts 26 | chaseCount ++; 27 | if (chase) { 28 | if (chaseCount > 2000) { 29 | chase = false; 30 | chaseCount = 0; 31 | } 32 | } else { 33 | if (chaseCount > 700) { 34 | chase = true; 35 | chaseCount = 0; 36 | } 37 | } 38 | 39 | 40 | 41 | if (deadForABit) { 42 | deadCount ++; 43 | if (deadCount > 300) { 44 | deadForABit = false; 45 | } 46 | } else {//if not deadforabit then show the ghost 47 | if (!frightened) { 48 | if (returnHome) {//have the ghost be transparent if on its way home 49 | stroke(colour, 100); 50 | fill(colour, 100); 51 | } else {// colour the ghost 52 | stroke(colour); 53 | fill(colour); 54 | } 55 | bestPath.show();//show the path the ghost is following 56 | } else {//if frightened 57 | flashCount ++; 58 | if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened 59 | frightened = false; 60 | flashCount = 0; 61 | } 62 | 63 | if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames 64 | stroke(255); 65 | fill(255); 66 | } else {//flash blue 67 | stroke(0, 0, 200); 68 | fill(0, 0, 200); 69 | } 70 | } 71 | ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle 72 | } 73 | } 74 | 75 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 76 | //moves the ghost along the path 77 | void move() { 78 | if (!deadForABit) {//dont move if dead 79 | pos.add(vel); 80 | checkDirection();//check if need to change direction next move 81 | } 82 | } 83 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 84 | 85 | //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path 86 | void setPath() { 87 | ghostNodes.clear(); 88 | setNodes(); 89 | start = ghostNodes.get(0); 90 | end = ghostNodes.get(ghostNodes.size()-1); 91 | Path temp = AStar(start, end, vel); 92 | if (temp!= null) {//if not path is found then dont change bestPath 93 | bestPath = temp.clone(); 94 | } 95 | } 96 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 97 | //sets all the nodes and connects them with adjacent nodes 98 | //also sets the target node 99 | void setNodes() { 100 | 101 | ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node 102 | for (int i = 1; i< 27; i++) {//check every position 103 | for (int j = 1; j< 30; j++) { 104 | //if there is a space up or below and a space left or right then this space is a node 105 | if (!tiles[j][i].wall) { 106 | if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space 107 | if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space 108 | 109 | ghostNodes.add(new Node(i, j));//add the nodes 110 | } 111 | } 112 | } 113 | } 114 | } 115 | if (returnHome) {//if returning home then the target is just above the ghost room thing 116 | ghostNodes.add(new Node(13, 11)); 117 | } else { 118 | if (chase) { 119 | ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16));//target pacman 120 | } else { 121 | ghostNodes.add(new Node(1, 1));//scatter to corner 122 | } 123 | } 124 | 125 | for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together 126 | ghostNodes.get(i).addEdges(ghostNodes); 127 | } 128 | } 129 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 130 | //check if the ghost needs to change direction as well as other stuff 131 | void checkDirection() { 132 | if (pacman.hitPacman(pos)) {//if hit pacman 133 | if (frightened) {//eaten by pacman 134 | returnHome = true; 135 | frightened = false; 136 | } else if (!returnHome) {//killPacman 137 | pacman.kill(); 138 | } 139 | } 140 | 141 | 142 | // check if reached home yet 143 | if (returnHome) { 144 | if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { 145 | //set the ghost as dead for a bit 146 | returnHome = false; 147 | deadForABit = true; 148 | deadCount = 0; 149 | } 150 | } 151 | 152 | if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position 153 | 154 | PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position 155 | 156 | if (frightened) {//no path needs to generated by the ghost if frightened 157 | boolean isNode = false; 158 | for (int j = 0; j < ghostNodes.size(); j++) { 159 | if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { 160 | isNode = true; 161 | } 162 | } 163 | if (!isNode) {//if not on a node then no need to do anything 164 | return; 165 | } else {//if on a node 166 | //set a random direction 167 | PVector newVel = new PVector(); 168 | int rand = floor(random(4)); 169 | switch(rand) { 170 | case 0: 171 | newVel = new PVector(1, 0); 172 | break; 173 | case 1: 174 | newVel = new PVector(0, 1); 175 | break; 176 | case 2: 177 | newVel = new PVector(-1, 0); 178 | break; 179 | case 3: 180 | newVel = new PVector(0, -1); 181 | break; 182 | } 183 | //if the random velocity is into a wall or in the opposite direction then choose another one 184 | while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { 185 | rand = floor(random(4)); 186 | switch(rand) { 187 | case 0: 188 | newVel = new PVector(1, 0); 189 | break; 190 | case 1: 191 | newVel = new PVector(0, 1); 192 | break; 193 | case 2: 194 | newVel = new PVector(-1, 0); 195 | break; 196 | case 3: 197 | newVel = new PVector(0, -1); 198 | break; 199 | } 200 | } 201 | vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed 202 | } 203 | } else {//not frightened 204 | 205 | setPath(); 206 | 207 | for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path 208 | if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { 209 | 210 | vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); 211 | vel.limit(1); 212 | 213 | return; 214 | } 215 | } 216 | } 217 | } 218 | } 219 | } -------------------------------------------------------------------------------- /PacmanGame/Clyde.pde: -------------------------------------------------------------------------------- 1 | class Clyde { 2 | PVector pos = new PVector(1*16 +8, 29*16+8); 3 | PVector vel = new PVector(1, 0); 4 | Path bestPath; // the variable stores the path the ghost will be following 5 | ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position 6 | Node start;//the ghosts position as a node 7 | Node end; //the ghosts target position as a node 8 | color colour = color(255, 100, 0);//orange 9 | 10 | boolean chase = true;//true if the ghost is in chase mode false if in scatter mode 11 | boolean frightened = false;//true if the ghost is in frightened mode 12 | int flashCount = 0;//in order to make the ghost flash when frightened this is a counter 13 | int chaseCount = 0;//counter for the switch between chase and scatter 14 | boolean returnHome = false;//if eaten return home 15 | boolean deadForABit = false;//after the ghost returns home it disappears for a bit 16 | int deadCount = 0; 17 | 18 | 19 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 20 | //constructor 21 | Clyde() { 22 | setPath(); 23 | } 24 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 25 | 26 | void show() { 27 | //increments counts 28 | chaseCount ++; 29 | if (chase) { 30 | if (chaseCount > 2000) { 31 | chase = false; 32 | chaseCount = 0; 33 | } 34 | } else { 35 | if (chaseCount > 700) { 36 | chase = true; 37 | chaseCount = 0; 38 | } 39 | } 40 | 41 | 42 | 43 | if (deadForABit) { 44 | deadCount ++; 45 | if (deadCount > 300) { 46 | deadForABit = false; 47 | } 48 | } else {//if not deadforabit then show the ghost 49 | if (!frightened) { 50 | if (returnHome) {//have the ghost be transparent if on its way home 51 | stroke(colour, 100); 52 | fill(colour, 100); 53 | } else {// colour the ghost 54 | stroke(colour); 55 | fill(colour); 56 | } 57 | bestPath.show();//show the path the ghost is following 58 | } else {//if frightened 59 | flashCount ++; 60 | if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened 61 | frightened = false; 62 | flashCount = 0; 63 | } 64 | 65 | if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames 66 | stroke(255); 67 | fill(255); 68 | } else {//flash blue 69 | stroke(0, 0, 200); 70 | fill(0, 0, 200); 71 | } 72 | } 73 | ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle 74 | } 75 | } 76 | 77 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 78 | //moves the ghost along the path 79 | void move() { 80 | if (!deadForABit) {//dont move if dead 81 | pos.add(vel); 82 | checkDirection();//check if need to change direction next move 83 | } 84 | } 85 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 86 | 87 | //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path 88 | void setPath() { 89 | ghostNodes.clear(); 90 | setNodes(); 91 | start = ghostNodes.get(0); 92 | end = ghostNodes.get(ghostNodes.size()-1); 93 | Path temp = AStar(start, end, vel); 94 | if (temp!= null) {//if not path is found then dont change bestPath 95 | bestPath = temp.clone(); 96 | } 97 | } 98 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 99 | //sets all the nodes and connects them with adjacent nodes 100 | //also sets the target node 101 | void setNodes() { 102 | 103 | ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node 104 | for (int i = 1; i< 27; i++) {//check every position 105 | for (int j = 1; j< 30; j++) { 106 | //if there is a space up or below and a space left or right then this space is a node 107 | if (!tiles[j][i].wall) { 108 | if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space 109 | if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space 110 | 111 | ghostNodes.add(new Node(i, j));//add the nodes 112 | } 113 | } 114 | } 115 | } 116 | } 117 | if (returnHome) {//if returning home then the target is just above the ghost room thing 118 | ghostNodes.add(new Node(13, 11)); 119 | } else { 120 | if (chase) { 121 | if (dist((pos.x-8)/16, (pos.y-8)/16, (pacman.pos.x-8) / 16, (pacman.pos.y-8)/16) > 8) { 122 | 123 | ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); 124 | } else { 125 | 126 | ghostNodes.add(new Node(1, 29)); 127 | } 128 | } else {//scatter 129 | ghostNodes.add(new Node(1, 29)); 130 | } 131 | } 132 | 133 | for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together 134 | ghostNodes.get(i).addEdges(ghostNodes); 135 | } 136 | } 137 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 138 | //check if the ghost needs to change direction as well as other stuff 139 | void checkDirection() { 140 | if (pacman.hitPacman(pos)) {//if hit pacman 141 | if (frightened) {//eaten by pacman 142 | returnHome = true; 143 | frightened = false; 144 | } else if (!returnHome) {//killPacman 145 | pacman.kill(); 146 | } 147 | } 148 | 149 | 150 | // check if reached home yet 151 | if (returnHome) { 152 | if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { 153 | //set the ghost as dead for a bit 154 | returnHome = false; 155 | deadForABit = true; 156 | deadCount = 0; 157 | } 158 | } 159 | 160 | if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position 161 | 162 | PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position 163 | 164 | if (frightened) {//no path needs to generated by the ghost if frightened 165 | boolean isNode = false; 166 | for (int j = 0; j < ghostNodes.size(); j++) { 167 | if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { 168 | isNode = true; 169 | } 170 | } 171 | if (!isNode) {//if not on a node then no need to do anything 172 | return; 173 | } else {//if on a node 174 | //set a random direction 175 | PVector newVel = new PVector(); 176 | int rand = floor(random(4)); 177 | switch(rand) { 178 | case 0: 179 | newVel = new PVector(1, 0); 180 | break; 181 | case 1: 182 | newVel = new PVector(0, 1); 183 | break; 184 | case 2: 185 | newVel = new PVector(-1, 0); 186 | break; 187 | case 3: 188 | newVel = new PVector(0, -1); 189 | break; 190 | } 191 | //if the random velocity is into a wall or in the opposite direction then choose another one 192 | while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { 193 | rand = floor(random(4)); 194 | switch(rand) { 195 | case 0: 196 | newVel = new PVector(1, 0); 197 | break; 198 | case 1: 199 | newVel = new PVector(0, 1); 200 | break; 201 | case 2: 202 | newVel = new PVector(-1, 0); 203 | break; 204 | case 3: 205 | newVel = new PVector(0, -1); 206 | break; 207 | } 208 | } 209 | vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed 210 | } 211 | } else {//not frightened 212 | 213 | setPath(); 214 | 215 | for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path 216 | if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { 217 | 218 | vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); 219 | vel.limit(1); 220 | 221 | return; 222 | } 223 | } 224 | } 225 | } 226 | } 227 | } -------------------------------------------------------------------------------- /PacmanGame/Inky.pde: -------------------------------------------------------------------------------- 1 | class Inky { 2 | PVector pos = new PVector(8*16 +8, 1*16+8);//starting position 3 | PVector vel = new PVector(1, 0); 4 | Path bestPath; // the variable stores the path the ghost will be following 5 | ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position 6 | Node start;//the ghosts position as a node 7 | Node end; //the ghosts target position as a node 8 | color colour = color(135, 206, 250);//blueish 9 | 10 | 11 | boolean chase = true;//true if the ghost is in chase mode false if in scatter mode 12 | boolean frightened = false;//true if the ghost is in frightened mode 13 | int flashCount = 0;//in order to make the ghost flash when frightened this is a counter 14 | int chaseCount = 0;//counter for the switch between chase and scatter 15 | boolean returnHome = false;//if eaten return home 16 | boolean deadForABit = false;//after the ghost returns home it disappears for a bit 17 | int deadCount = 0; 18 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 19 | //constructor 20 | Inky() { 21 | setPath(); 22 | } 23 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 24 | 25 | void show() { 26 | //increments counts 27 | chaseCount ++; 28 | if (chase) { 29 | if (chaseCount > 2000) { 30 | chase = false; 31 | chaseCount = 0; 32 | } 33 | } else { 34 | if (chaseCount > 700) { 35 | chase = true; 36 | chaseCount = 0; 37 | } 38 | } 39 | 40 | 41 | 42 | if (deadForABit) { 43 | deadCount ++; 44 | if (deadCount > 300) { 45 | deadForABit = false; 46 | } 47 | } else {//if not deadforabit then show the ghost 48 | if (!frightened) { 49 | if (returnHome) {//have the ghost be transparent if on its way home 50 | stroke(colour, 100); 51 | fill(colour, 100); 52 | } else {// colour the ghost 53 | stroke(colour); 54 | fill(colour); 55 | } 56 | bestPath.show();//show the path the ghost is following 57 | } else {//if frightened 58 | flashCount ++; 59 | if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened 60 | frightened = false; 61 | flashCount = 0; 62 | } 63 | 64 | if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames 65 | stroke(255); 66 | fill(255); 67 | } else {//flash blue 68 | stroke(0, 0, 200); 69 | fill(0, 0, 200); 70 | } 71 | } 72 | ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle 73 | } 74 | } 75 | 76 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 77 | //moves the ghost along the path 78 | void move() { 79 | if (!deadForABit) {//dont move if dead 80 | pos.add(vel); 81 | checkDirection();//check if need to change direction next move 82 | } 83 | } 84 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 85 | 86 | //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path 87 | void setPath() { 88 | ghostNodes.clear(); 89 | setNodes(); 90 | start = ghostNodes.get(0); 91 | end = ghostNodes.get(ghostNodes.size()-1); 92 | Path temp = AStar(start, end, vel); 93 | if (temp!= null) {//if not path is found then dont change bestPath 94 | bestPath = temp.clone(); 95 | } 96 | } 97 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 98 | //sets all the nodes and connects them with adjacent nodes 99 | //also sets the target node 100 | void setNodes() { 101 | 102 | ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node 103 | for (int i = 1; i< 27; i++) {//check every position 104 | for (int j = 1; j< 30; j++) { 105 | //if there is a space up or below and a space left or right then this space is a node 106 | if (!tiles[j][i].wall) { 107 | if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space 108 | if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space 109 | 110 | ghostNodes.add(new Node(i, j));//add the nodes 111 | } 112 | } 113 | } 114 | } 115 | } 116 | if (returnHome) {//if returning home then the target is just above the ghost room thing 117 | ghostNodes.add(new Node(13, 11)); 118 | } else { 119 | if (chase) { 120 | PVector pacmanPosition = new PVector((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16); 121 | PVector blinkyPosition = new PVector((blinky.pos.x-8)/16, (blinky.pos.y - 8)/16); 122 | PVector blinkyToPacman = new PVector(pacmanPosition.x - blinkyPosition.x, pacmanPosition.y - blinkyPosition.y); 123 | 124 | PVector target = new PVector (pacmanPosition.x + blinkyToPacman.x, pacmanPosition.y + blinkyToPacman.y); 125 | PVector nearestTile = getNearestNonWallTile(target); 126 | 127 | if (dist((pos.x-8)/16, (pos.y-8)/16, nearestTile.x, nearestTile.y)<1) { 128 | ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); 129 | } else { 130 | 131 | 132 | ghostNodes.add(new Node(nearestTile.x, nearestTile.y)); 133 | } 134 | } else {//scatter 135 | ghostNodes.add(new Node(26, 29)); 136 | } 137 | } 138 | 139 | for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together 140 | ghostNodes.get(i).addEdges(ghostNodes); 141 | } 142 | } 143 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 144 | //check if the ghost needs to change direction as well as other stuff 145 | void checkDirection() { 146 | if (pacman.hitPacman(pos)) {//if hit pacman 147 | if (frightened) {//eaten by pacman 148 | returnHome = true; 149 | frightened = false; 150 | } else if (!returnHome) {//killPacman 151 | pacman.kill(); 152 | } 153 | } 154 | 155 | 156 | // check if reached home yet 157 | if (returnHome) { 158 | if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { 159 | //set the ghost as dead for a bit 160 | returnHome = false; 161 | deadForABit = true; 162 | deadCount = 0; 163 | } 164 | } 165 | 166 | if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position 167 | 168 | PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position 169 | 170 | if (frightened) {//no path needs to generated by the ghost if frightened 171 | boolean isNode = false; 172 | for (int j = 0; j < ghostNodes.size(); j++) { 173 | if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { 174 | isNode = true; 175 | } 176 | } 177 | if (!isNode) {//if not on a node then no need to do anything 178 | return; 179 | } else {//if on a node 180 | //set a random direction 181 | PVector newVel = new PVector(); 182 | int rand = floor(random(4)); 183 | switch(rand) { 184 | case 0: 185 | newVel = new PVector(1, 0); 186 | break; 187 | case 1: 188 | newVel = new PVector(0, 1); 189 | break; 190 | case 2: 191 | newVel = new PVector(-1, 0); 192 | break; 193 | case 3: 194 | newVel = new PVector(0, -1); 195 | break; 196 | } 197 | //if the random velocity is into a wall or in the opposite direction then choose another one 198 | while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { 199 | rand = floor(random(4)); 200 | switch(rand) { 201 | case 0: 202 | newVel = new PVector(1, 0); 203 | break; 204 | case 1: 205 | newVel = new PVector(0, 1); 206 | break; 207 | case 2: 208 | newVel = new PVector(-1, 0); 209 | break; 210 | case 3: 211 | newVel = new PVector(0, -1); 212 | break; 213 | } 214 | } 215 | vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed 216 | } 217 | } else {//not frightened 218 | 219 | setPath(); 220 | 221 | for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path 222 | if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { 223 | 224 | vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); 225 | vel.limit(1); 226 | 227 | return; 228 | } 229 | } 230 | } 231 | } 232 | } 233 | } -------------------------------------------------------------------------------- /PacmanGame/Node.pde: -------------------------------------------------------------------------------- 1 | class Node { 2 | 3 | LinkedList edges = new LinkedList();//all the nodes this node is connected to 4 | float x; 5 | float y; 6 | float smallestDistToPoint = 10000000;//the distance of smallest path from the start to this node 7 | int degree; 8 | int value; 9 | boolean checked = false; 10 | //------------------------------------------------------------------------------------------------------------------------------------------------- 11 | //constructor 12 | Node(float x1, float y1) { 13 | x = x1; 14 | y = y1; 15 | } 16 | //------------------------------------------------------------------------------------------------------------------------------------------------- 17 | //draw a litle circle 18 | void show() { 19 | fill(0, 100, 100); 20 | ellipse(x*16 +8, y*16 +8, 10, 10 ); 21 | } 22 | 23 | //------------------------------------------------------------------------------------------------------------------------------------------------- 24 | //add all the nodes this node is adjacent to 25 | void addEdges(ArrayList nodes) { 26 | for (int i =0; i < nodes.size(); i++) {//for all the nodes 27 | if (nodes.get(i).y == y ^ nodes.get(i).x == x) { 28 | if (nodes.get(i).y == y) {//if the node is on the same line horizontally 29 | float mostLeft = min(nodes.get(i).x, x) + 1; 30 | float max = max(nodes.get(i).x, x); 31 | boolean edge = true; 32 | while (mostLeft < max) {//look from the one node to the other looking for a wall 33 | if (tiles[(int)y][(int)mostLeft].wall) { 34 | edge = false;//not an edge since there is a wall in the way 35 | break; 36 | } 37 | mostLeft ++;//move 1 step closer to the other node 38 | } 39 | if (edge) { 40 | edges.add(nodes.get(i));//add the node as an edge 41 | } 42 | } else if (nodes.get(i).x == x) {//same line vertically 43 | float mostUp = min(nodes.get(i).y, y) + 1; 44 | float max = max(nodes.get(i).y, y); 45 | boolean edge = true; 46 | while (mostUp < max) { 47 | if (tiles[(int)mostUp][(int)x].wall) { 48 | edge = false; 49 | break; 50 | } 51 | mostUp ++; 52 | } 53 | if (edge) { 54 | edges.add(nodes.get(i)); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /PacmanGame/Pacman.pde: -------------------------------------------------------------------------------- 1 | class Pacman { 2 | PVector pos; 3 | PVector vel = new PVector(-1, 0); 4 | 5 | //when pacman reaches a node its velocity changes to the value stored in turnto 6 | PVector turnTo = new PVector(-1, 0); 7 | boolean turn = false; 8 | int score = 0; 9 | int lives = 2; 10 | boolean gameOver = false; 11 | //--------------------------------------------------------------------------------------------------------------------------------------------------------- 12 | //constructor 13 | Pacman() { 14 | pos = new PVector(13*16+8, 23*16 +8); 15 | } 16 | 17 | //--------------------------------------------------------------------------------------------------------------------------------------------------------- 18 | 19 | //draws pacman 20 | void show() { 21 | fill(255, 255, 0); 22 | stroke(255, 255, 0); 23 | ellipse(pos.x, pos.y, 20, 20); 24 | } 25 | 26 | //--------------------------------------------------------------------------------------------------------------------------------------------------------- 27 | //move pacman if not facing wall 28 | void move() { 29 | if (checkPosition()) { 30 | pos.add(vel); 31 | } 32 | } 33 | 34 | //--------------------------------------------------------------------------------------------------------------------------------------------------------- 35 | 36 | //returns whether the input vector hits pacman 37 | boolean hitPacman(PVector GhostPos) { 38 | if (dist(GhostPos.x, GhostPos.y, pos.x, pos.y) < 10) { 39 | return true; 40 | } 41 | return false; 42 | } 43 | 44 | 45 | //--------------------------------------------------------------------------------------------------------------------------------------------------------- 46 | //called when a ghost hits pacman 47 | void kill() { 48 | lives -=1; 49 | if (lives < 0) {//game over if no lives left 50 | gameOver = true; 51 | } else { 52 | pos = new PVector(13*16+8, 23*16 +8); //reset positions 53 | 54 | blinky = new Blinky(); 55 | clyde = new Clyde(); 56 | pinky = new Pinky(); 57 | inky = new Inky(); 58 | vel = new PVector(-1, 0); 59 | turnTo = new PVector(-1, 0); 60 | } 61 | } 62 | 63 | //------------------------------------------------------------------------------------------------------------------------------------------------- 64 | //returns whether pacman can move i.e. there is no wall in the direction of vel 65 | boolean checkPosition() { 66 | 67 | if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position 68 | 69 | PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position 70 | 71 | //reset all the paths for all the ghosts 72 | blinky.setPath(); 73 | pinky.setPath(); 74 | clyde.setPath(); 75 | inky.setPath(); 76 | 77 | //check if the position has been eaten or not, note the blank spaces are initialised as already eaten 78 | if (!tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten) { 79 | tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten =true; 80 | score +=1;//add a point 81 | if (tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].bigDot) {//if big dot eaten 82 | //set all ghosts to frightened 83 | blinky.frightened = true; 84 | blinky.flashCount = 0; 85 | clyde.frightened = true; 86 | clyde.flashCount = 0; 87 | pinky.frightened = true; 88 | pinky.flashCount = 0; 89 | inky.frightened = true; 90 | inky.flashCount = 0; 91 | } 92 | } 93 | 94 | 95 | PVector positionToCheck= new PVector(matrixPosition.x + turnTo.x, matrixPosition.y+ turnTo.y); // the position in the tiles double array that the player is turning towards 96 | 97 | if (tiles[floor(positionToCheck.y)][floor(positionToCheck.x)].wall) {//check if there is a free space in the direction that it is going to turn 98 | if (tiles[floor(matrixPosition.y + vel.y)][floor(matrixPosition.x + vel.x)].wall) {//if not check if the path ahead is free 99 | return false;//if neither are free then dont move 100 | } else {//forward is free 101 | return true; 102 | } 103 | } else {//free to turn 104 | vel = new PVector(turnTo.x, turnTo.y); 105 | return true; 106 | } 107 | } else { 108 | if ((pos.x+10*vel.x-8)%16 == 0 && (pos.y + 10*vel.y - 8)% 16 ==0) {//if 10 places off a critical position in the direction that pacman is moving 109 | PVector matrixPosition = new PVector((pos.x+10*vel.x-8)/16, (pos.y+10*vel.y-8)/16);//convert that position to an array position 110 | if (!tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten ) {//if that tile has not been eaten 111 | tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten =true;//eat it 112 | score +=1; 113 | println("Score:", score); 114 | if (tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].bigDot) {//big dot eaten 115 | //set all ghosts as frightened 116 | blinky.frightened = true; 117 | blinky.flashCount = 0; 118 | clyde.frightened = true; 119 | clyde.flashCount = 0; 120 | pinky.frightened = true; 121 | pinky.flashCount = 0; 122 | inky.frightened = true; 123 | inky.flashCount = 0; 124 | } 125 | } 126 | } 127 | if (turnTo.x + vel.x == 0 && vel.y + turnTo.y ==0) {//if turning chenging directions entirely i.e. 180 degree turn 128 | vel = new PVector(turnTo.x, turnTo.y);//turn 129 | return true; 130 | } 131 | return true;//if not on a critical postion then continue forward 132 | } 133 | 134 | } 135 | } -------------------------------------------------------------------------------- /PacmanGame/PacmanGame.pde: -------------------------------------------------------------------------------- 1 | //import stuff for pathfinding 2 | import java.util.Deque; 3 | import java.util.Iterator; 4 | import java.util.LinkedList; 5 | 6 | Pacman pacman; 7 | PImage img;//background image 8 | 9 | Pinky pinky; 10 | Blinky blinky; 11 | Clyde clyde; 12 | Inky inky; 13 | Tile[][] tiles = new Tile[31][28]; //note it goes y then x because of how I inserted the data 14 | int[][] tilesRepresentation = { 15 | {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 16 | {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 17 | {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, 18 | {1, 8, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 8, 1}, 19 | {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, 20 | {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 21 | {1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1}, 22 | {1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1}, 23 | {1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1}, 24 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 6, 1, 1, 6, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 25 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 6, 1, 1, 6, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 26 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 27 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 28 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 29 | {1, 1, 1, 1, 1, 1, 0, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 0, 1, 1, 1, 1, 1, 1}, 30 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 31 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 32 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 33 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 34 | {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, 35 | {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 36 | {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, 37 | {1, 8, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 8, 1}, 38 | {1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1}, 39 | {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1}, 40 | {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1}, 41 | {1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1}, 42 | {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, 43 | {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, 44 | {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 45 | {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};//its not sexy but it does the job 46 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 47 | 48 | void setup() { 49 | frameRate(100); 50 | size(448, 496); 51 | img = loadImage("map.jpg"); 52 | //initiate tiles 53 | for (int i = 0; i< 28; i++) { 54 | for (int j = 0; j< 31; j++) { 55 | tiles[j][i] = new Tile(16*i +8, 16*j+8); 56 | switch(tilesRepresentation[j][i]) { 57 | case 1: //1 is a wall 58 | tiles[j][i].wall = true; 59 | break; 60 | case 0: // 0 is a dot 61 | tiles[j][i].dot = true; 62 | break; 63 | case 8: // 8 is a big dot 64 | tiles[j][i].bigDot = true; 65 | break; 66 | case 6://6 is a blank space 67 | tiles[j][i].eaten = true; 68 | break; 69 | } 70 | } 71 | } 72 | 73 | pacman = new Pacman(); 74 | pinky = new Pinky(); 75 | blinky = new Blinky(); 76 | clyde = new Clyde(); 77 | inky = new Inky(); 78 | } 79 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 80 | 81 | void draw() { 82 | image(img, 0, 0); 83 | if (!pacman.gameOver) { 84 | stroke(255); 85 | 86 | for (int i = 0; i< 28; i++) { 87 | for (int j = 0; j< 31; j++) { 88 | tiles[j][i].show(); 89 | } 90 | } 91 | pacman.move(); 92 | 93 | //move and show the ghosts 94 | inky.show(); 95 | inky.move(); 96 | 97 | clyde.show(); 98 | clyde.move(); 99 | 100 | pinky.show(); 101 | pinky.move(); 102 | 103 | blinky.show(); 104 | blinky.move(); 105 | 106 | //show pacman last so he appears over the path lines 107 | pacman.show(); 108 | } 109 | } 110 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 111 | 112 | void keyPressed() {//controls for pacman 113 | switch(key) { 114 | case CODED: 115 | switch(keyCode) { 116 | case UP: 117 | pacman.turnTo = new PVector(0, -1); 118 | pacman.turn = true; 119 | break; 120 | case DOWN: 121 | pacman.turnTo = new PVector(0, 1); 122 | pacman.turn = true; 123 | break; 124 | case LEFT: 125 | pacman.turnTo = new PVector(-1, 0); 126 | pacman.turn = true; 127 | break; 128 | case RIGHT: 129 | pacman.turnTo = new PVector(1, 0); 130 | pacman.turn = true; 131 | break; 132 | } 133 | } 134 | } 135 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 136 | 137 | 138 | //returns the nearest non wall tile to the input vector 139 | //input is in tile coordinates 140 | PVector getNearestNonWallTile(PVector target) { 141 | float min = 1000; 142 | int minIndexj = 0; 143 | int minIndexi = 0; 144 | for (int i = 0; i< 28; i++) {//for each tile 145 | for (int j = 0; j< 31; j++) { 146 | if (!tiles[j][i].wall) {//if its not a wall 147 | if (dist(i, j, target.x, target.y) big = new LinkedList();//stores all paths 164 | Path extend = new Path(); //a temp Path which is to be extended by adding another node 165 | Path winningPath = new Path(); //the final path 166 | Path extended = new Path(); //the extended path 167 | LinkedList sorting = new LinkedList();///used for sorting paths by their distance to the target 168 | 169 | //startin off with big storing a path with only the starting node 170 | extend.addToTail(start, finish); 171 | extend.velAtLast = new PVector(vel.x, vel.y);//used to prevent ghosts from doing a u turn 172 | big.add(extend); 173 | 174 | 175 | boolean winner = false;//has a path from start to finish been found 176 | 177 | while (true) //repeat the process until ideal path is found or there is not path found 178 | { 179 | extend = big.pop();//grab the front path form the big to be extended 180 | if (extend.path.getLast().equals(finish)) //if goal found 181 | { 182 | if (!winner) //if first goal found, set winning path 183 | { 184 | winner = true; 185 | winningPath = extend.clone(); 186 | } else { //if current path found the goal in a shorter distance than the previous winner 187 | if (winningPath.distance > extend.distance) 188 | { 189 | winningPath = extend.clone();//set this path as the winning path 190 | } 191 | } 192 | if (big.isEmpty()) //if this extend is the last path then return the winning path 193 | { 194 | return winningPath.clone(); 195 | } else {//if not the current extend is useless to us as it cannot be extended since its finished 196 | extend = big.pop();//so get the next path 197 | } 198 | } 199 | 200 | 201 | //if the final node in the path has already been checked and the distance to it was shorter than this path has taken to get there than this path is no good 202 | if (!extend.path.getLast().checked || extend.distance < extend.path.getLast().smallestDistToPoint) 203 | { 204 | if (!winner || extend.distance + dist(extend.path.getLast().x, extend.path.getLast().y, finish.x, finish.y) < winningPath.distance) //dont look at paths that are longer than a path which has already reached the goal 205 | { 206 | 207 | //if this is the first path to reach this node or the shortest path to reach this node then set the smallest distance to this point to the distance of this path 208 | extend.path.getLast().smallestDistToPoint = extend.distance; 209 | 210 | //move all paths to sorting form big then add the new paths (in the for loop)and sort them back into big. 211 | sorting = (LinkedList)big.clone(); 212 | Node tempN = new Node(0, 0);//reset temp node 213 | if (extend.path.size() >1) { 214 | tempN = extend.path.get(extend.path.size() -2);//set the temp node to be the second last node in the path 215 | } 216 | 217 | for (int i =0; i< extend.path.getLast().edges.size(); i++) //for each node incident (connected) to the final node of the path to be extended 218 | { 219 | if (tempN != extend.path.getLast().edges.get(i))//if not going backwards i.e. the new node is not the previous node behind it 220 | { 221 | 222 | //if the direction to the new node is in the opposite to the way the path was heading then dont count this path 223 | PVector directionToNode = new PVector( extend.path.getLast().edges.get(i).x -extend.path.getLast().x, extend.path.getLast().edges.get(i).y - extend.path.getLast().y ); 224 | directionToNode.limit(vel.mag()); 225 | if (directionToNode.x == -1* extend.velAtLast.x && directionToNode.y == -1* extend.velAtLast.y ) { 226 | } else {//if not turning around 227 | extended = extend.clone(); 228 | extended.addToTail(extend.path.getLast().edges.get(i), finish); 229 | extended.velAtLast = new PVector(directionToNode.x, directionToNode.y); 230 | sorting.add(extended.clone());//add this extended list to the list of paths to be sorted 231 | } 232 | } 233 | } 234 | 235 | 236 | //sorting now contains all the paths form big plus the new paths which where extended 237 | //adding the path which has the higest distance to big first so that its at the back of big. 238 | //using selection sort i.e. the easiest and worst sorting algorithm 239 | big.clear(); 240 | while (!sorting.isEmpty()) 241 | { 242 | float max = -1; 243 | int iMax = 0; 244 | for (int i = 0; i < sorting.size(); i++) 245 | { 246 | if (max < sorting.get(i).distance + sorting.get(i).distToFinish)//A* uses the distance from the goal plus the paths length to determine the sorting order 247 | { 248 | iMax = i; 249 | max = sorting.get(i).distance + sorting.get(i).distToFinish; 250 | } 251 | } 252 | big.addFirst(sorting.remove(iMax).clone());//add it to the front so that the ones with the greatest distance end up at the back 253 | //and the closest ones end up at the front 254 | } 255 | } 256 | extend.path.getLast().checked = true; 257 | } 258 | //if no more paths avaliable 259 | if (big.isEmpty()) { 260 | if (winner ==false) //there is not path from start to finish 261 | { 262 | print("FUCK!!!!!!!!!!");//error message 263 | return null; 264 | } else {//if winner is found then the shortest winner is stored in winning path so return that 265 | return winningPath.clone(); 266 | } 267 | } 268 | } 269 | } -------------------------------------------------------------------------------- /PacmanGame/Path.pde: -------------------------------------------------------------------------------- 1 | class Path { 2 | LinkedList path = new LinkedList();//a list of nodes 3 | float distance = 0;//length of path 4 | float distToFinish =0;//the distance between the final node and the paths goal 5 | PVector velAtLast;//the direction the ghost is going at the last point on the path 6 | 7 | //-------------------------------------------------------------------------------------------------------------------------------------------- 8 | //constructor 9 | Path() { 10 | } 11 | //-------------------------------------------------------------------------------------------------------------------------------------------- 12 | //adds a node to the end of the path 13 | void addToTail(Node n, Node endNode) 14 | { 15 | if (!path.isEmpty())//if path is empty then this is the first node and thus the distance is still 0 16 | { 17 | distance += dist(path.getLast().x, path.getLast().y, n.x, n.y);//add the distance from the current last element in the path to the new node to the overall distance 18 | } 19 | 20 | path.add(n);//add the node 21 | distToFinish = dist(path.getLast().x, path.getLast().y, endNode.x, endNode.y);//recalculate the distance to the finish 22 | } 23 | //-------------------------------------------------------------------------------------------------------------------------------------------- 24 | //retrun a clone of this 25 | Path clone() 26 | { 27 | Path temp = new Path(); 28 | temp.path = (LinkedList)path.clone(); 29 | temp.distance = distance; 30 | temp.distToFinish = distToFinish; 31 | temp.velAtLast = new PVector(velAtLast.x, velAtLast.y); 32 | return temp; 33 | } 34 | //-------------------------------------------------------------------------------------------------------------------------------------------- 35 | //removes all nodes in the path 36 | void clear() 37 | { 38 | distance =0; 39 | distToFinish = 0; 40 | path.clear(); 41 | } 42 | //-------------------------------------------------------------------------------------------------------------------------------------------- 43 | //draw lines representing the path 44 | void show() { 45 | strokeWeight(2); 46 | for (int i = 0; i< path.size()-1; i++) { 47 | line(path.get(i).x*16 +8, path.get(i).y*16 +8, path.get(i+1).x*16 +8, path.get(i+1).y*16 +8);// 48 | } 49 | ellipse((path.get(path.size() -1).x*16)+8, (path.get(path.size() -1).y*16)+8, 5, 5); 50 | } 51 | } -------------------------------------------------------------------------------- /PacmanGame/Pinky.pde: -------------------------------------------------------------------------------- 1 | class Pinky { 2 | PVector pos = new PVector(23*16 +8, 26*16+8); 3 | PVector vel = new PVector(1, 0); 4 | Path bestPath; // the variable stores the path the ghost will be following 5 | ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position 6 | Node start;//the ghosts position as a node 7 | Node end; //the ghosts target position as a node 8 | color colour = color(255, 0, 255);//pink 9 | 10 | 11 | boolean chase = true;//true if the ghost is in chase mode false if in scatter mode 12 | boolean frightened = false;//true if the ghost is in frightened mode 13 | int flashCount = 0;//in order to make the ghost flash when frightened this is a counter 14 | int chaseCount = 0;//counter for the switch between chase and scatter 15 | boolean returnHome = false;//if eaten return home 16 | boolean deadForABit = false;//after the ghost returns home it disappears for a bit 17 | int deadCount = 0; 18 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 19 | //constructor 20 | Pinky() { 21 | setPath(); 22 | } 23 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 24 | 25 | void show() { 26 | //increments counts 27 | chaseCount ++; 28 | if (chase) { 29 | if (chaseCount > 2000) { 30 | chase = false; 31 | chaseCount = 0; 32 | } 33 | } else { 34 | if (chaseCount > 700) { 35 | chase = true; 36 | chaseCount = 0; 37 | } 38 | } 39 | 40 | 41 | 42 | if (deadForABit) { 43 | deadCount ++; 44 | if (deadCount > 300) { 45 | deadForABit = false; 46 | } 47 | } else {//if not deadforabit then show the ghost 48 | if (!frightened) { 49 | if (returnHome) {//have the ghost be transparent if on its way home 50 | stroke(colour, 100); 51 | fill(colour, 100); 52 | } else {// colour the ghost 53 | stroke(colour); 54 | fill(colour); 55 | } 56 | bestPath.show();//show the path the ghost is following 57 | } else {//if frightened 58 | flashCount ++; 59 | if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened 60 | frightened = false; 61 | flashCount = 0; 62 | } 63 | 64 | if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames 65 | stroke(255); 66 | fill(255); 67 | } else {//flash blue 68 | stroke(0, 0, 200); 69 | fill(0, 0, 200); 70 | } 71 | } 72 | ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle 73 | } 74 | } 75 | 76 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 77 | //moves the ghost along the path 78 | void move() { 79 | if (!deadForABit) {//dont move if dead 80 | pos.add(vel); 81 | checkDirection();//check if need to change direction next move 82 | } 83 | } 84 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 85 | 86 | //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path 87 | void setPath() { 88 | ghostNodes.clear(); 89 | setNodes(); 90 | start = ghostNodes.get(0); 91 | end = ghostNodes.get(ghostNodes.size()-1); 92 | Path temp = AStar(start, end, vel); 93 | if (temp!= null) {//if not path is found then dont change bestPath 94 | bestPath = temp.clone(); 95 | } 96 | } 97 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 98 | //sets all the nodes and connects them with adjacent nodes 99 | //also sets the target node 100 | void setNodes() { 101 | 102 | ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node 103 | for (int i = 1; i< 27; i++) {//check every position 104 | for (int j = 1; j< 30; j++) { 105 | //if there is a space up or below and a space left or right then this space is a node 106 | if (!tiles[j][i].wall) { 107 | if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space 108 | if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space 109 | 110 | ghostNodes.add(new Node(i, j));//add the nodes 111 | } 112 | } 113 | } 114 | } 115 | } 116 | if (returnHome) {//if returning home then the target is just above the ghost room thing 117 | ghostNodes.add(new Node(13, 11)); 118 | } else { 119 | if (chase) { 120 | //pinky moves towards 4 positions ahead of pacman 121 | int lookAhead = 4; 122 | PVector pacmanMatrixPos = new PVector((pacman.pos.x-8) / 16 + (pacman.vel.x *lookAhead), (pacman.pos.y-8 )/16 +(pacman.vel.y *lookAhead)); 123 | while (!(pacmanMatrixPos.x > 0 && pacmanMatrixPos.y >0 && pacmanMatrixPos.x <28 124 | && pacmanMatrixPos.y < 31 && !tiles[(int)pacmanMatrixPos.y][(int)pacmanMatrixPos.x].wall)) { 125 | lookAhead -=1; 126 | pacmanMatrixPos = new PVector((pacman.pos.x-8) / 16 + (pacman.vel.x *lookAhead), (pacman.pos.y-8 )/16 +(pacman.vel.y *lookAhead)); 127 | } 128 | if (dist((pos.x-8)/16, (pos.y-8)/16, pacmanMatrixPos.x, pacmanMatrixPos.y)<1) { 129 | ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); 130 | } else { 131 | 132 | ghostNodes.add(new Node(pacmanMatrixPos.x, pacmanMatrixPos.y));//(pacman.pos.x-8 + (pacman.vel.x *4)) / 16, (pacman.pos.y-8 +(pacman.vel.y *4))/16)); 133 | } 134 | } else {//scatter 135 | ghostNodes.add(new Node(26, 1)); 136 | } 137 | } 138 | 139 | for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together 140 | ghostNodes.get(i).addEdges(ghostNodes); 141 | } 142 | } 143 | //-------------------------------------------------------------------------------------------------------------------------------------------------- 144 | //check if the ghost needs to change direction as well as other stuff 145 | void checkDirection() { 146 | if (pacman.hitPacman(pos)) {//if hit pacman 147 | if (frightened) {//eaten by pacman 148 | returnHome = true; 149 | frightened = false; 150 | } else if (!returnHome) {//killPacman 151 | pacman.kill(); 152 | } 153 | } 154 | 155 | 156 | // check if reached home yet 157 | if (returnHome) { 158 | if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { 159 | //set the ghost as dead for a bit 160 | returnHome = false; 161 | deadForABit = true; 162 | deadCount = 0; 163 | } 164 | } 165 | 166 | if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position 167 | 168 | PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position 169 | 170 | if (frightened) {//no path needs to generated by the ghost if frightened 171 | boolean isNode = false; 172 | for (int j = 0; j < ghostNodes.size(); j++) { 173 | if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { 174 | isNode = true; 175 | } 176 | } 177 | if (!isNode) {//if not on a node then no need to do anything 178 | return; 179 | } else {//if on a node 180 | //set a random direction 181 | PVector newVel = new PVector(); 182 | int rand = floor(random(4)); 183 | switch(rand) { 184 | case 0: 185 | newVel = new PVector(1, 0); 186 | break; 187 | case 1: 188 | newVel = new PVector(0, 1); 189 | break; 190 | case 2: 191 | newVel = new PVector(-1, 0); 192 | break; 193 | case 3: 194 | newVel = new PVector(0, -1); 195 | break; 196 | } 197 | //if the random velocity is into a wall or in the opposite direction then choose another one 198 | while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { 199 | rand = floor(random(4)); 200 | switch(rand) { 201 | case 0: 202 | newVel = new PVector(1, 0); 203 | break; 204 | case 1: 205 | newVel = new PVector(0, 1); 206 | break; 207 | case 2: 208 | newVel = new PVector(-1, 0); 209 | break; 210 | case 3: 211 | newVel = new PVector(0, -1); 212 | break; 213 | } 214 | } 215 | vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed 216 | } 217 | } else {//not frightened 218 | 219 | setPath(); 220 | 221 | for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path 222 | if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { 223 | 224 | vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); 225 | vel.limit(1); 226 | 227 | return; 228 | } 229 | } 230 | } 231 | } 232 | } 233 | } -------------------------------------------------------------------------------- /PacmanGame/Tile.pde: -------------------------------------------------------------------------------- 1 | class Tile { 2 | boolean wall = false; 3 | boolean dot = false; 4 | boolean bigDot = false; 5 | boolean eaten = false; 6 | PVector pos; 7 | //------------------------------------------------------------------------------------------------------------------------------------------- 8 | //constructor 9 | Tile(float x, float y) { 10 | pos = new PVector(x, y); 11 | } 12 | //----------------------------------------------------------------------------------------------------------------------------------------------- 13 | //draw a dot if there is one in this tile 14 | void show() { 15 | if (dot) { 16 | if (!eaten) {//draw dot 17 | fill(255, 255, 0); 18 | noStroke(); 19 | ellipse(pos.x, pos.y, 3, 3); 20 | } 21 | } else if (bigDot) { 22 | if (!eaten) {//draw big dot 23 | fill(255, 255, 0); 24 | noStroke(); 25 | ellipse(pos.x, pos.y, 6, 6); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /PacmanGame/data/map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-Bullet/PacmanGame/dda54626ad59fabec6ef2a494204170b1a7344fb/PacmanGame/data/map.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PacmanGame 2 | I built the pacman game without the ai for pacman 3 | 4 | I used processing to write this code and if you wanna run it then you will need processing 5 | https://processing.org/ 6 | 7 | --------------------------------------------------------------------------------