Browse Source

support more touches

pull/3880/head
blueloveTH 9 months ago
parent
commit
9f7dec63ef
3 changed files with 169 additions and 139 deletions
  1. +80
    -108
      projects/Xcode15/main.c
  2. +4
    -4
      projects/Xcode15/raylib.xcodeproj/project.pbxproj
  3. +85
    -27
      src/platforms/rcore_ios.c

+ 80
- 108
projects/Xcode15/main.c View File

@ -1,148 +1,120 @@
/*******************************************************************************************
*
* raylib [core] examples - basic screen manager
* raylib [core] example - Input Gestures Detection
*
* NOTE: This example illustrates a very simple screen manager based on a states machines
*
* Example originally created with raylib 4.0, last time updated with raylib 4.0
* Example originally created with raylib 1.4, last time updated with raylib 4.2
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2021-2024 Ramon Santamaria (@raysan5)
* Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib.h"
//------------------------------------------------------------------------------------------
// Types and Structures Definition
//------------------------------------------------------------------------------------------
typedef enum GameScreen { LOGO = 0, TITLE, GAMEPLAY, ENDING } GameScreen;
int MAX_GESTURE_STRINGS = 20;
int screenWidth = 0;
int screenHeight = 0;
GameScreen currentScreen = LOGO;
int framesCounter = 0; // Useful to count frames
Vector2 touchPosition;
Rectangle touchArea;
int gesturesCount = 0;
char gestureStrings[100][32];
int currentGesture = GESTURE_NONE;
int lastGesture = GESTURE_NONE;
void ios_ready(){
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(0, 0, "raylib [core] example - basic screen manager");
InitWindow(0, 0, "raylib [core] example - input gestures");
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
// TODO: Initialize all required variables and load all required data here!
SetTargetFPS(60); // Set desired framerate (frames-per-second)
//--------------------------------------------------------------------------------------
touchPosition = (Vector2){ 0, 0 };
touchArea = (Rectangle){ 220, 10, screenWidth - 230.0f, screenHeight - 20.0f };
//SetGesturesEnabled(0b0000000000001001); // Enable only some gestures to be detected
SetTargetFPS(60);
MAX_GESTURE_STRINGS = (screenHeight - 50) / 20;
}
static void add_gesture(const char* title){
if (gesturesCount >= MAX_GESTURE_STRINGS)
{
for (int i = 0; i < MAX_GESTURE_STRINGS; i++) TextCopy(gestureStrings[i], "\0");
gesturesCount = 0;
}
TextCopy(gestureStrings[gesturesCount], title);
gesturesCount++;
}
void ios_update()
{
// Update
//----------------------------------------------------------------------------------
switch(currentScreen)
lastGesture = currentGesture;
currentGesture = GetGestureDetected();
touchPosition = GetTouchPosition(0);
if(IsMouseButtonPressed(0)) add_gesture("MouseButtonPressed");
if(IsMouseButtonReleased(0)) add_gesture("MouseButtonReleased");
if (CheckCollisionPointRec(touchPosition, touchArea) && (currentGesture != GESTURE_NONE))
{
case LOGO:
{
// TODO: Update LOGO screen variables here!
framesCounter++; // Count frames
// Wait for 2 seconds (120 frames) before jumping to TITLE screen
if (framesCounter > 120)
{
currentScreen = TITLE;
}
} break;
case TITLE:
{
// TODO: Update TITLE screen variables here!
// Press enter to change to GAMEPLAY screen
if (IsKeyPressed(KEY_ENTER) || IsGestureDetected(GESTURE_TAP))
{
currentScreen = GAMEPLAY;
}
} break;
case GAMEPLAY:
{
// TODO: Update GAMEPLAY screen variables here!
// Press enter to change to ENDING screen
if (IsKeyPressed(KEY_ENTER) || IsGestureDetected(GESTURE_TAP))
{
currentScreen = ENDING;
}
} break;
case ENDING:
if (currentGesture != lastGesture)
{
// TODO: Update ENDING screen variables here!
// Press enter to return to TITLE screen
if (IsKeyPressed(KEY_ENTER) || IsGestureDetected(GESTURE_TAP))
// Store gesture string
switch (currentGesture)
{
currentScreen = TITLE;
case GESTURE_TAP: add_gesture( "GESTURE TAP"); break;
case GESTURE_DOUBLETAP: add_gesture( "GESTURE DOUBLETAP"); break;
case GESTURE_HOLD: add_gesture( "GESTURE HOLD"); break;
case GESTURE_DRAG: add_gesture( "GESTURE DRAG"); break;
case GESTURE_SWIPE_RIGHT: add_gesture( "GESTURE SWIPE RIGHT"); break;
case GESTURE_SWIPE_LEFT: add_gesture( "GESTURE SWIPE LEFT"); break;
case GESTURE_SWIPE_UP: add_gesture( "GESTURE SWIPE UP"); break;
case GESTURE_SWIPE_DOWN: add_gesture( "GESTURE SWIPE DOWN"); break;
case GESTURE_PINCH_IN: add_gesture( "GESTURE PINCH IN"); break;
case GESTURE_PINCH_OUT: add_gesture( "GESTURE PINCH OUT"); break;
default: break;
}
} break;
default: break;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
switch(currentScreen)
DrawRectangleRec(touchArea, GRAY);
DrawRectangle(225, 15, screenWidth - 240, screenHeight - 30, RAYWHITE);
DrawText("GESTURES TEST AREA", screenWidth - 270, screenHeight - 40, 20, Fade(GRAY, 0.5f));
for (int i = 0; i < gesturesCount; i++)
{
case LOGO:
{
// TODO: Draw LOGO screen here!
DrawText("LOGO SCREEN", 20, 20, 40, LIGHTGRAY);
DrawText("WAIT for 2 SECONDS...", 290, 220, 20, GRAY);
} break;
case TITLE:
{
// TODO: Draw TITLE screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, GREEN);
DrawText("TITLE SCREEN", 20, 20, 40, DARKGREEN);
DrawText("PRESS ENTER or TAP to JUMP to GAMEPLAY SCREEN", 120, 220, 20, DARKGREEN);
} break;
case GAMEPLAY:
{
// TODO: Draw GAMEPLAY screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, PURPLE);
DrawText("GAMEPLAY SCREEN", 20, 20, 40, MAROON);
DrawText("PRESS ENTER or TAP to JUMP to ENDING SCREEN", 130, 220, 20, MAROON);
} break;
case ENDING:
{
// TODO: Draw ENDING screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, BLUE);
DrawText("ENDING SCREEN", 20, 20, 40, DARKBLUE);
DrawText("PRESS ENTER or TAP to RETURN to TITLE SCREEN", 120, 220, 20, DARKBLUE);
} break;
default: break;
if (i % 2 == 0){
DrawRectangle(10, 30 + 20*i, 200, 20, Fade(LIGHTGRAY, 0.5f));
}else{
DrawRectangle(10, 30 + 20*i, 200, 20, Fade(LIGHTGRAY, 0.3f));
}
if (i < gesturesCount - 1){
DrawText(gestureStrings[i], 35, 36 + 20*i, 10, DARKGRAY);
}else{
DrawText(gestureStrings[i], 35, 36 + 20*i, 10, MAROON);
}
}
DrawRectangleLines(10, 29, 200, screenHeight - 50, GRAY);
DrawText(
TextFormat("TOUCH COUNT: %d", GetTouchPointCount()),
50, 15, 10, GRAY
);
for(int i=0; i < GetTouchPointCount(); i++){
DrawCircleV(GetTouchPosition(i), 30, MAROON);
}
DrawFPS(screenWidth / 2, 0);
EndDrawing();
//----------------------------------------------------------------------------------
}
void ios_destroy(){
// De-Initialization
//--------------------------------------------------------------------------------------
// TODO: Unload all loaded data (textures, fonts, audio) here!
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

+ 4
- 4
projects/Xcode15/raylib.xcodeproj/project.pbxproj View File

@ -354,9 +354,9 @@
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "";
INFOPLIST_KEY_UIRequiredDeviceCapabilities = metal;
INFOPLIST_KEY_UIRequiresFullScreen = YES;
INFOPLIST_KEY_UIStatusBarHidden = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -396,9 +396,9 @@
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "";
INFOPLIST_KEY_UIRequiredDeviceCapabilities = metal;
INFOPLIST_KEY_UIRequiresFullScreen = YES;
INFOPLIST_KEY_UIStatusBarHidden = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

+ 85
- 27
src/platforms/rcore_ios.c View File

@ -1,13 +1,13 @@
/**********************************************************************************************
*
* rcore_<platform> template - Functions to manage window, graphics device and inputs
* rcore_ios - Functions to manage window, graphics device and touch inputs
*
* PLATFORM: IOS
* - iOS (arm64)
*
* LIMITATIONS:
* - Limitation 01
* - Limitation 02
* - No keyboard input support
* - No gamepad input support
*
* POSSIBLE IMPROVEMENTS:
* - Improvement 01
@ -21,7 +21,7 @@
* Custom flag for rcore on target platform -not used-
*
* DEPENDENCIES:
* - o"><platform-specific SDK dependency>
* - n">ANGLE for iOS
* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
*
*
@ -48,6 +48,7 @@
// TODO: Include the platform specific libraries
#include "libEGL/libEGL.h"
#include <complex.h>
// iOS only supports callbacks
// We are not able to give users full control of the game loop
@ -60,11 +61,6 @@ extern void ios_destroy();
/* GameViewController */
@interface GameViewController : UIViewController
- (void)update;
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
//- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches;
@end
/* AppDelegate */
@ -661,7 +657,28 @@ void ClosePlatform(void)
ios_update();
}
void call_gesture(int action)
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
// In iOS 17, Messages allows you to interactively resize iMessage apps with a vertical pan gesture. Messages handles any conflicts between resize gestures and your custom gestures. If your app uses manual touch handling, override those methods in your apps UIView. You can either change your manual touch handling code to use a gesture recognizer instead, or your UIView can override gestureRecognizerShouldBegin: and return NO when your iMessage app doesnt own the gesture.
return false;
}
static void sync_all_touches(UIEvent* event)
{
CORE.Input.Touch.pointCount = event.allTouches.count;
int i = 0;
for (UITouch *touch in event.allTouches)
{
CGPoint location = [touch locationInView:platform.viewController.view];
CORE.Input.Touch.position[i] = (Vector2){ location.x, location.y };
CORE.Input.Touch.pointId[i] = (int)touch;
i++;
if(i >= MAX_TOUCH_POINTS) break;
}
// TODO: Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height
}
static void send_gesture_event(NSSet<UITouch *> * touches, int action)
{
#if defined(SUPPORT_GESTURES_SYSTEM)
GestureEvent gestureEvent = { 0 };
@ -682,30 +699,65 @@ void call_gesture(int action)
// Gesture data is sent to gestures system for processing
ProcessGestureEvent(gestureEvent);
#endif
if(action == TOUCH_ACTION_UP){
// One of the touchpoints is released, remove it from touch point arrays
for (UITouch *touch in touches)
{
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
if(CORE.Input.Touch.pointId[i] == (int)touch){
CORE.Input.Touch.pointId[i] = 0;
CORE.Input.Touch.position[i] = (Vector2){ 0.0f, 0.0f };
break;
}
}
}
// re-arrange the touch points
int j = 0;
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
if(CORE.Input.Touch.pointId[i] != 0){
CORE.Input.Touch.pointId[j] = CORE.Input.Touch.pointId[i];
CORE.Input.Touch.position[j] = CORE.Input.Touch.position[i];
j++;
}
}
CORE.Input.Touch.pointCount -= touches.count;
}
if(action == TOUCH_ACTION_MOVE){
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}else{
CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
}
// Map touch[0] as mouse input for convenience
CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
}
// touch callbacks
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CORE.Input.Touch.pointCount += touches.count;
if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
for(UITouch* touch in touches) call_gesture(TOUCH_ACTION_DOWN);
sync_all_touches(event);
send_gesture_event(touches, TOUCH_ACTION_DOWN);
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CORE.Input.Touch.pointCount -= touches.count;
if (CORE.Input.Touch.pointCount == 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
for(UITouch* touch in touches) call_gesture(TOUCH_ACTION_UP);
sync_all_touches(event);
n">send_gesture_event(touches, TOUCH_ACTION_UP);
o">// post sync needed
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
for(UITouch* touch in touches) call_gesture(TOUCH_ACTION_MOVE);
sync_all_touches(event);
send_gesture_event(touches, TOUCH_ACTION_MOVE);
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CORE.Input.Touch.pointCount = 0;
CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
for(UITouch* touch in touches) call_gesture(TOUCH_ACTION_CANCEL);
sync_all_touches(event);
send_gesture_event(touches, TOUCH_ACTION_CANCEL);
}
@end
@ -718,9 +770,7 @@ void call_gesture(int action)
self.window.backgroundColor = [UIColor redColor];
self.window.rootViewController = [[GameViewController alloc] init];
[self.window makeKeyAndVisible];
ios_ready();
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self.window.rootViewController selector:@selector(update)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
return YES;
@ -729,8 +779,12 @@ void call_gesture(int action)
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
}
@ -739,12 +793,16 @@ void call_gesture(int action)
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
ios_destroy();
if (platform.device != EGL_NO_DISPLAY)
{
// the user does not call CloseWindow() before exiting
TRACELOG(LOG_ERROR, "DISPLAY: CloseWindow() should be called before terminating the application");
}
// If 'platform.device' is already set to 'EGL_NO_DISPLAY'
// this means that the user has already called 'CloseWindow()'
}
@end

Loading…
Cancel
Save