├── README.md └── cart.p8 /README.md: -------------------------------------------------------------------------------- 1 | # pico-8-for-beginners 2 | Code for the PICO-8 for beginners workshop! 3 | -------------------------------------------------------------------------------- /cart.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 16 3 | __lua__ 4 | 5 | local score 6 | local game_objects 7 | 8 | function _init() 9 | -- start the score counter at zero 10 | score=0 11 | -- create the game objects 12 | game_objects={} 13 | -- create a player 14 | make_player(64,24) 15 | -- create some coins 16 | local i 17 | for i=1,3 do 18 | make_coin(30+15*i,80) 19 | end 20 | -- create some blocks 21 | for i=1,14 do 22 | make_block(8*i,90) 23 | end 24 | make_block(50,50) 25 | make_block(90,70) 26 | make_block(30,82) 27 | end 28 | 29 | function _update() 30 | local obj 31 | -- update all the game objects 32 | for obj in all(game_objects) do 33 | obj:update() 34 | end 35 | end 36 | 37 | function _draw() 38 | -- clear the screen 39 | cls() 40 | -- draw the score 41 | print(score,5,5,7) 42 | -- draw all the game objects 43 | local obj 44 | for obj in all(game_objects) do 45 | obj:draw() 46 | end 47 | end 48 | 49 | 50 | -- game object creation functions 51 | function make_player(x,y) 52 | return make_game_object("player",x,y,{ 53 | width=8, 54 | height=8, 55 | move_speed=1, 56 | is_standing_on_block=false, 57 | is_facing_left=false, 58 | walk_counter=0, 59 | update=function(self) 60 | -- update walk counter 61 | if self.walk_counter==0 then 62 | self.walk_counter=8 63 | else 64 | self.walk_counter-=1 65 | end 66 | -- apply friction 67 | self.velocity_x*=0.2 68 | -- move the player with the arrow keys 69 | if btn(1) then 70 | self.velocity_x=self.move_speed 71 | self.is_facing_left=false 72 | end 73 | if btn(0) then 74 | self.velocity_x=-self.move_speed 75 | self.is_facing_left=true 76 | end 77 | -- jump when z is pressed 78 | if btn(4) and self.is_standing_on_block then 79 | self.velocity_y=-3 80 | sfx(3) 81 | end 82 | -- apply gravity 83 | self.velocity_y+=0.1 84 | -- make sure the velocity doesn't get too big 85 | self.velocity_x=mid(-3,self.velocity_x,3) 86 | self.velocity_y=mid(-3,self.velocity_y,3) 87 | -- apply the velocity 88 | self.x+=self.velocity_x 89 | self.y+=self.velocity_y 90 | -- check to see if hitting coins 91 | for_each_game_object("coin",function(coin) 92 | if self:check_for_hit(coin) and not coin.is_collected then 93 | coin.is_collected=true 94 | score+=1 95 | sfx(4) 96 | end 97 | end) 98 | -- check to see if colliding with blocks 99 | local was_standing_on_block=self.is_standing_on_block 100 | self.is_standing_on_block=false 101 | for_each_game_object("block",function(block) 102 | local collision_dir=self:check_for_collision(block,3.1) 103 | self:handle_collision(block,collision_dir) 104 | if collision_dir=="down" then 105 | self.is_standing_on_block=true 106 | if not was_standing_on_block then 107 | sfx(5) 108 | end 109 | end 110 | end) 111 | end, 112 | draw=function(self) 113 | -- self:draw_bounding_box(7) 114 | local sprite_num 115 | if self.is_standing_on_block then 116 | if self.velocity_x==mid(-0.1,self.velocity_x,0.1) then 117 | sprite_num=8 118 | elseif self.walk_counter<4 then 119 | sprite_num=9 120 | else 121 | sprite_num=10 122 | end 123 | else 124 | if self.velocity_y>0 then 125 | sprite_num=12 126 | else 127 | sprite_num=11 128 | end 129 | end 130 | spr(sprite_num,self.x,self.y,1,1,self.is_facing_left) 131 | end 132 | }) 133 | end 134 | 135 | function make_block(x,y) 136 | return make_game_object("block",x,y,{ 137 | width=8, 138 | height=8, 139 | draw=function(self) 140 | spr(6,self.x,self.y) 141 | -- rect(self.x,self.y,self.x+self.width,self.y+self.height,8) 142 | end 143 | }) 144 | end 145 | 146 | function make_coin(x,y) 147 | return make_game_object("coin",x,y,{ 148 | width=6, 149 | height=7, 150 | is_collected=false, 151 | draw=function(self) 152 | if not self.is_collected then 153 | spr(7,self.x,self.y) 154 | -- rect(self.x,self.y,self.x+self.width,self.y+self.height,12) 155 | end 156 | end 157 | }) 158 | end 159 | 160 | function make_game_object(name,x,y,props) 161 | local obj={ 162 | name=name, 163 | x=x, 164 | y=y, 165 | velocity_x=0, 166 | velocity_y=0, 167 | update=function(self) 168 | -- do nothing 169 | end, 170 | draw=function(self) 171 | -- don't draw anything 172 | end, 173 | draw_bounding_box=function(self,color) 174 | rect(self.x,self.y,self.x+self.width,self.y+self.height,color) 175 | end, 176 | center=function(self) 177 | return self.x+self.width/2,self.y+self.height/2 178 | end, 179 | check_for_hit=function(self,other) 180 | return bounding_boxes_overlapping(self,other) 181 | end, 182 | check_for_collision=function(self,other,indent) 183 | local x,y,w,h=self.x,self.y,self.width,self.height 184 | local top_hitbox={x=x+indent,y=y,width=w-2*indent,height=h/2} 185 | local bottom_hitbox={x=x+indent,y=y+h/2,width=w-2*indent,height=h/2} 186 | local left_hitbox={x=x,y=y+indent,width=w/2,height=h-2*indent} 187 | local right_hitbox={x=x+w/2,y=y+indent,width=w/2,height=h-2*indent} 188 | if bounding_boxes_overlapping(bottom_hitbox,other) then 189 | return "down" 190 | elseif bounding_boxes_overlapping(left_hitbox,other) then 191 | return "left" 192 | elseif bounding_boxes_overlapping(right_hitbox,other) then 193 | return "right" 194 | elseif bounding_boxes_overlapping(top_hitbox,other) then 195 | return "up" 196 | end 197 | end, 198 | handle_collision=function(self,other,dir) 199 | if dir=="down" then 200 | self.y=other.y-self.height 201 | if self.velocity_y>0 then 202 | self.velocity_y=0 203 | end 204 | elseif dir=="left" then 205 | self.x=other.x+other.width 206 | if self.velocity_x<0 then 207 | self.velocity_x=0 208 | end 209 | elseif dir=="right" then 210 | self.x=other.x-self.width 211 | if self.velocity_x>0 then 212 | self.velocity_x=0 213 | end 214 | elseif dir=="up" then 215 | self.y=other.y+other.height 216 | if self.velocity_y<0 then 217 | self.velocity_y=0 218 | end 219 | end 220 | end 221 | } 222 | -- add additional properties 223 | local key,value 224 | for key,value in pairs(props) do 225 | obj[key]=value 226 | end 227 | -- add it to the list of game objects 228 | add(game_objects,obj) 229 | -- return the game object 230 | return obj 231 | end 232 | 233 | 234 | -- hit detection helper functions 235 | function rects_overlapping(left1,top1,right1,bottom1,left2,top2,right2,bottom2) 236 | return right1>left2 and right2>left1 and bottom1>top2 and bottom2>top1 237 | end 238 | 239 | function bounding_boxes_overlapping(obj1,obj2) 240 | return rects_overlapping(obj1.x,obj1.y,obj1.x+obj1.width,obj1.y+obj1.height,obj2.x,obj2.y,obj2.x+obj2.width,obj2.y+obj2.height) 241 | end 242 | 243 | 244 | function for_each_game_object(name,callback) 245 | local obj 246 | for obj in all(game_objects) do 247 | if obj.name==name then 248 | callback(obj) 249 | end 250 | end 251 | end 252 | 253 | __gfx__ 254 | 000000000000000000000000000000000000b0000000000077777777000000000003000000030000000300000003000000030000000000000000000000000000 255 | 00000000000000000000000000000000000b3000000000007666666d009aa0000033330000333300003333000033330000333300000000000000000000000000 256 | 00000000000000000000000000000000099b3940000000007677776d09aaaa000099990000999900009999000094940000999900000000000000000000000000 257 | 00000000000000000000000000000000994949940000000076766d6d09aaaa000094940000949400009494000099994400999900000000000000000000000000 258 | 00000000000000000000000000000000949994940000000076766d6d09aaaa000099994400999944009999440099990000949400000000000000000000000000 259 | 00000000000000000000000000000000949994990000000076dddd6d09aaaa000099990000999900009999000099990000999944000000000000000000000000 260 | 0000000000000000000000000000000094999499000000007666666d009aa0000044440000444400004444000444444004444440000000000000000000000000 261 | 000000000000000000000000000000000949949000000000dddddddd000000000040040004000040000440000000000000000000000000000000000000000000 262 | __sfx__ 263 | 010a0000180501a0501c0502405024050240502405024050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 264 | 011300001b0661b0661b0661b0641b0671b0641b0630d0000d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 265 | 01100000180551a0551c0552405024040240302402024010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 266 | 0108000011440114411344114431174311b4211f42125411004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400 267 | 01070000180551c0551f0552405024031240110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 268 | 011000001144000400004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400004000040000400 269 | --------------------------------------------------------------------------------