Arranged for Pac-Man created based on the published Specifications I want to add. Let's make Pac-Man not only swipe but also touch and accelerometer, and add an original maze. The source code is available on GitHub, so please refer to it.
Add a configuration menu to switch between operations and mazes. For the maze to be added, refer to the following specifications under development.
Add a menu to CgSceneCreditMode. After a while after inserting the credit, the entrance of the menu will be displayed in case 1. If you touch it, you can enter the menu.
For the structure of the CgSceneFrame class, please refer to the previous [Introduction] iOS App Development # 5 [Sequence Design].
/// Credit Mode
class CgSceneCreditMode : CgSceneFrame {
enum EnEvent: Int {
case EnterConfig = 3
case Operation = 5
case ExtraMode = 6
case DebugMode = 7
case Language = 8
case ResetSetting = 9
case ExitConfig = 10
case None
}
private let table_enterConfiguration: [(Int,Int,Int,Int,EnEvent)] = [
( 26, 34, 28, 36, .EnterConfig)
]
private let table_setConfiguration: [(Int,Int,Int,Int,EnEvent)] = [
( 26, 34, 28, 36, .ExitConfig),
( 4, 25, 28, 27, .Operation),
( 4, 20, 28, 22, .ExtraMode),
( 4, 15, 28, 17, .DebugMode),
( 4, 10, 28, 12, .Language),
( 4, 5, 28, 7, .ResetSetting)
]
private var table_search: [(column0:Int,row0:Int,column1:Int,row1:Int,event:EnEvent)] = []
private var configMode: Bool = false
/// Event handler
/// - Parameters:
/// - sender: Message sender
/// - id: Message ID
/// - values: Parameters of message
override func handleEvent(sender: CbObject, message: EnMessage, parameter values: [Int]) {
if message == .Touch {
let position = CgPosition.init(x: CGFloat(values[0]), y: CGFloat(values[1]))
let event = search(column: position.column, row: position.row)
if event == .None {
if !configMode {
stopSequence()
}
} else {
goToNextSequence(event.rawValue)
}
}
}
/// Handle sequence
/// To override in a derived class.
/// - Parameter sequence: Sequence number
/// - Returns: If true, continue the sequence, if not, end the sequence.
override func handleSequence(sequence: Int) -> Bool {
switch sequence {
case 0:
configMode = false
table_search = []
clear()
printFrame()
printPlayerScore()
printHighScore()
printCredit()
printRounds()
background.print(0, color: .Orange, column: 6, row: 19, string: "PUSH START BUTTON")
background.print(0, color: .Cyan, column: 8, row: 15, string: "1 PLAYER ONLY")
if context.language == .English {
background.print(0, color: .Pink, column: 1, row: 11, string: "BONUS PAC-MAN FOR 20000 ]^_")
background.print(0, color: .Purple, column:4, row: 4, string: "@ 2020 HIWAY.KIKUTADA")
} else {
background.print(0, color: .Pink, column: 1, row: 11, string: "BONUS PAC-MAN FOR 10000 ]^_")
background.print(0, color: .Purple, column: 7, row: 7, string: "@ #$%&'()* 2020") // NAMACO
}
goToNextSequence(after: 60*16*2)
case 1:
table_search = table_enterConfiguration
background.print(0, color: .White, column: 27, row: 35, string: "$")
goToNextSequence()
case 2:
// wait for event
break;
case 3:
configMode = true
table_search = table_setConfiguration
background.fill(0, texture: 0)
printFrame()
printPlayerScore()
printHighScore()
printCredit()
background.print(0, color: .White, column: 27, row: 35, string: "#")
background.print(0, color: .Red, column: 8, row: 31, string: "CONFIGURATION")
background.print(0, color: .White, column: 4, row: 26, string: "OPERATION")
background.print(0, color: .White, column: 4, row: 21, string: "EXTRA MODE")
background.print(0, color: .White, column: 4, row: 16, string: "DEBUG MODE")
background.print(0, color: .White, column: 4, row: 11, string: "LANGUAGE")
background.print(0, color: .White, column: 4, row: 6, string: "SETTING")
print_operation(color: .Pink)
print_extraMode(color: .Pink)
print_debugMode(color: .Pink)
print_language(color: .Pink)
print_resetSetting(color: .Pink)
goToNextSequence()
case 4:
// wait for event
break;
case 5: // operation
context.operationMode = context.operationMode.getNext()
print_operation(color: .Yellow)
goToNextSequence(4)
case 6: // ExtraMode/
context.extraMode = context.extraMode.getNext()
print_extraMode(color: .Yellow)
goToNextSequence(4)
case 7: // DebugMode
context.debugMode = context.debugMode.getNext()
print_debugMode(color: .Yellow)
goToNextSequence(4)
case 8: // Language
context.language = context.language.getNext()
print_language(color: .Yellow)
goToNextSequence(4)
case 9: // ResetSetting
context.resetSetting = context.resetSetting.getNext()
print_resetSetting(color: .Yellow)
goToNextSequence(4)
case 10:
context.saveConfiguration()
goToNextSequence(0)
default:
clear()
// Stop and exit running sequence.
return false
}
return true
}
func clear() {
background.fill(0, texture: 0)
}
func print_operation(color: CgCustomBackgroundManager.EnBgColor) {
let str = context.operationMode.getString()
background.print(0, color: color, column: 16, row: 26, string: str)
}
func print_extraMode(color: CgCustomBackgroundManager.EnBgColor) {
let str = context.extraMode.getString()
background.print(0, color: color, column: 16, row: 21, string: str)
}
func print_debugMode(color: CgCustomBackgroundManager.EnBgColor) {
let str = context.debugMode.getString()
background.print(0, color: color, column: 16, row: 16, string: str)
}
func print_language(color: CgCustomBackgroundManager.EnBgColor) {
let str = context.language.getString()
background.print(0, color: color, column: 16, row: 11, string: str)
}
func print_resetSetting(color: CgCustomBackgroundManager.EnBgColor) {
let str = context.resetSetting.getString()
background.print(0, color: color, column: 16, row: 6, string: str)
}
func search(column: Int, row: Int) -> EnEvent {
var event: EnEvent = .None
for i in 0 ..< table_search.count {
let t = table_search[i]
if (t.column0 <= column && t.row0 <= row) && (t.column1 >= column && t.row1 >= row) {
event = t.event
break
}
}
return event
}
}
When the Touch event is received by the handelEvent method, the range pressed by the search function is checked, and if applicable, the case of that sequence is executed.
The setting value is defined in CgContext class.
class CgContext {
enum EnOperationMode: Int {
case Swipe = 0, Touch, Accel
func getString() -> String {
switch self {
case .Swipe: return "(SWIPE)"
case .Touch: return "(TOUCH)"
case .Accel: return "(ACCEL)"
}
}
func getNext() -> EnOperationMode {
switch self {
case .Swipe: return .Touch
case .Touch: return .Accel
case .Accel: return .Swipe
}
}
}
enum EnLanguage: Int {
case English = 0, Japanese
func getString() -> String {
switch self {
case .English: return "(ENGLISH) "
case .Japanese: return "(JAPANESE)"
}
}
func getNext() -> EnLanguage {
switch self {
case .English: return .Japanese
case .Japanese: return .English
}
}
}
enum EnOnOff: Int {
case Off = 0, On
func getString() -> String {
switch self {
case .On: return "(ON) "
case .Off: return "(OFF)"
}
}
func getNext() -> EnOnOff {
switch self {
case .On: return .Off
case .Off: return .On
}
}
}
enum EnSetting: Int {
case Clear = 0, Keep
func getString() -> String {
switch self {
case .Clear: return "(CLEAR)"
case .Keep: return "(KEEP) "
}
}
func getNext() -> EnSetting {
switch self {
case .Clear: return .Keep
case .Keep: return .Clear
}
}
}
var operationMode: EnOperationMode = .Swipe
var extraMode: EnOnOff = .Off
var debugMode: EnOnOff = .Off
var resetSetting: EnSetting = .Clear
var language: EnLanguage = .Japanese
//Omitted below
class GameScene: SKScene {
/// Main object with main game sequence
private var gameMain: CgGameMain!
/// Points for Swipe operation
private var startPoint: CGPoint = CGPoint.init()
private var endPoint: CGPoint = CGPoint.init()
// MotionManager for accel
private var motionManager: CMMotionManager!
override func didMove(to view: SKView) {
// Create and start game sequence.
gameMain = CgGameMain(skscene: self)
gameMain.startSequence()
// Create motion manager
motionManager = CMMotionManager()
motionManager.accelerometerUpdateInterval = 0.05
motionManager.startAccelerometerUpdates(
to: OperationQueue.current!, withHandler: {
(accelData: CMAccelerometerData?, errorOC: Error?) in self.sendAccelEvent(acceleration: accelData!.acceleration)
}
)
}
func sendAccelEvent(acceleration: CMAcceleration){
let x_diff: Int = Int(acceleration.x * 100)
let y_diff: Int = Int(acceleration.y * 100)
if abs(x_diff) > abs(y_diff) {
gameMain.sendEvent(message: .Accel, parameter: [Int(x_diff > 0 ? EnDirection.Right.rawValue : EnDirection.Left.rawValue)])
} else {
gameMain.sendEvent(message: .Accel, parameter: [Int(y_diff > 0 ? EnDirection.Up.rawValue : EnDirection.Down.rawValue)])
}
}
Generate CMMotionManager in GameScene class and set the cycle to get the value in accelerometerUpdateInterval member, Set sendAccelEvent in the callback method.
The sendAccelEvent method is called in a 0.05s cycle, in which the direction is calculated from the tilt of the accelerometer, and the event is sent to the object with gameMain.sendEvent in the same way as the swipe operation.
class CgPlayer : CgActor {
/// Event handler
/// - Parameters:
/// - sender: Message sender
/// - id: Message ID
/// - values: Parameters of message
override func handleEvent(sender: CbObject, message: EnMessage, parameter values: [Int]) {
guard !deligateActor.isDemoMode() else { return }
switch message {
case .Accel where deligateActor.isOperationMode(mode: CgContext.EnOperationMode.Accel): fallthrough
case .Swipe where deligateActor.isOperationMode(mode: CgContext.EnOperationMode.Swipe):
if let direction = EnDirection(rawValue: values[0]) {
targetDirecition = direction
}
case .Touch where deligateActor.isOperationMode(mode: CgContext.EnOperationMode.Touch):
setTargetPosition(x: values[0], y: values[1])
targetDirecition = decideDirectionByTarget(forcedDirectionChange: true)
position.amountMoved = 0
default:
break
}
}
If the operation set in the configuration menu is valid in handleEvent of CgPlayer class (deligateActor.isOperationMode (mode: CgContext.EnOperationMode.Accel)), the event of the accelerometer is accepted.
For this, please refer to the previous [Introduction] iOS application development # 6 [Character operation].
Make the getMazeSource method of CgSceneMaze class switch the maze data by the value of the menu. Creating this maze data is classic but surprisingly easy.
func getMazeSource() -> [String] {
let mazeSource: [String] = [
"aggggggggggggjiggggggggggggb",
"e111111111111EF111111111111f",
"e1AGGB1AGGGB1EF1AGGGB1AGGB1f",
"e3E F1E F1EF1E F1E F3f",
"e1CHHD1CHHHD1CD1CHHHD1CHHD1f",
"e11111111111111111111111111f",
"e1AGGB1AB1AGGGGGGB1AB1AGGB1f",
"e1CHHD1EF1CHHJIHHD1EF1CHHD1f",
"e111111EF1111EF1111EF111111f",
"chhhhB1EKGGB1EF1AGGLF1Ahhhhd",
" e1EIHHD2CD2CHHJF1f ",
" e1EF EF1f ",
" e1EF QhUWWVhR EF1f ",
"gggggD1CD f e CD1Cggggg",
"____ 1 f e 1 ____" ,
"hhhhhB1AB f e AB1Ahhhhh",
" e1EF SggggggT EF1f ",
" e1EF EF1f ",
" e1EF AGGGGGGB EF1f ",
"aggggD1CD1CHHJIHHD1CD1Cggggb",
"e111111111111EF111111111111f",
"e1AGGB1AGGGB1EF1AGGGB1AGGB1f",
"e1CHJF1CHHHD2CD2CHHHD1EIHD1f",
"e311EF1111111 1111111EF113f",
"kGB1EF1AB1AGGGGGGB1AB1EF1AGl",
"YHD1CD1EF1CHHJIHHD1EF1CD1CHZ",
"e111111EF1111EF1111EF111111f",
"e1AGGGGLKGGB1EF1AGGLKGGGGB1f",
"e1CHHHHHHHHD1CD1CHHHHHHHHD1f",
"e11111111111111111111111111f",
"chhhhhhhhhhhhhhhhhhhhhhhhhhd"
]
let mazeSourceExtra1: [String] = [
"aggggjiggggggjiggggggjiggggb",
"e1111EF111111EF111111EF1111f",
"e1AB1EF1AGGB1CD1AGGB1EF1AB1f",
"e3EF1EF1E F1111E F1EF1EF3f",
"e1CD1CD1CHHD1AB1CHHD1CD1CD1f",
"e111111111111EF111111111111f",
"kGGB1AGGB1AGGLKGGB1AGGB1AGGl",
"YHJF1EIHD1CHHJIHHD1CHJF1EIHZ",
"e1EF1EF111111EF111111EF1EF1f",
"e1EF1EKGGGGB1EF1AGGGGLF1EF1f",
"e1CD1CHHHHHD2CD2CHHHHHD1CD1f",
"e11111111 11111111f",
"e1AB1AGGB QhUWWVhR AGGB1AB1f",
"e1EF1CHHD f e CHHD1EF1f",
"e1EF11111 f e 11111EF1f",
"kGLF1AGGB f e AGGB1EKGl",
"YHHD1EIHD SggggggT CHJF1CHHZ",
"e1111EF11 11EF1111f",
"e1AGGLF1AGGGGGGGGGGB1EKGGB1f",
"e1CHHJF1CHHHHJIHHHHD1EIHHD1f",
"e1111EF111111EF111111EF1111f",
"kGGB1EKGGGGB1EF1AGGGGLF1AGGl",
"YHHD1CHHHHHD2CD2CHHHHHD1CHHZ",
"e111111111111 111111111111f",
"e1AGGGB1AGGGGGGGGGGB1AGGGB1f",
"e1CHHJF1CHHHHJIHHHHD1EIHHD1f",
"e3111EF111111EF111111EF1113f",
"kGGB1EF1AB1AGLKGB1AB1EF1AGGl",
"YHHD1CD1EF1CHHHHD1EF1CD1CHHZ",
"e1111111EF11111111EF1111111f",
"chhhhhhhnmhhhhhhhhnmhhhhhhhd"
]
return context.extraMode == CgContext.EnOnOff.Off ? mazeSource : mazeSourceExtra1
}
The following has been added as an arrangement of the Pac-Man game this time. --Configuration menu --Accelerometer, touch operation --Original maze
I've been studying iOS programming with Swift based on Pac-Man games. The source code was about 5,000 lines including comments, and it was possible to easily realize the quality and arrangement equivalent to an arcade game.
It started from the summer vacation where I couldn't go anywhere in Corona, but it was another good opportunity to enjoy programming.
This is the end of all 11 introductions to iOS application development.
Thank you for reading ~