{"id":21431874,"url":"https://github.com/dalae37/hitaball","last_synced_at":"2025-03-16T22:33:30.016Z","repository":{"id":92562173,"uuid":"187392684","full_name":"DaLae37/HitABall","owner":"DaLae37","description":"Ball hitting game like golf","archived":false,"fork":false,"pushed_at":"2023-10-04T04:20:44.000Z","size":17181,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-23T08:41:54.435Z","etag":null,"topics":["game","pygame","python","shooting-game"],"latest_commit_sha":null,"homepage":"https://www.dalae37.com/project/hitaball/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DaLae37.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-18T18:32:38.000Z","updated_at":"2023-10-04T04:43:55.000Z","dependencies_parsed_at":"2025-01-23T08:41:51.595Z","dependency_job_id":"484b3691-1450-4400-bf2e-84ff02f77749","html_url":"https://github.com/DaLae37/HitABall","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DaLae37%2FHitABall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DaLae37%2FHitABall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DaLae37%2FHitABall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DaLae37%2FHitABall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DaLae37","download_url":"https://codeload.github.com/DaLae37/HitABall/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243945002,"owners_count":20372885,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["game","pygame","python","shooting-game"],"created_at":"2024-11-22T23:15:16.679Z","updated_at":"2025-03-16T22:33:29.994Z","avatar_url":"https://github.com/DaLae37.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hit A Ball\n\n```The license applies only to the Python code, NOT image and sound files```\n\n## Proposal\n\n### 1.주제\n\n1. PyGame을 이용하여 게임 만들기\n\n2. PyGame프레임 워크를 개발하기\n\n3. 물리와 삼각함수를 게임에 적용시켜보기\n\n### 2.주제 선정 이유\n\n**1. 왜 PyGame을 이용하여 게임을 만드는가?**\n\n게임은 다양한 소프트웨어 기술들의 집합체입니다.\n\n그런 개발에 필요한 기술들을 파이썬 언어에서는 다양한 모듈들을 통해 손쉽게 사용할 수 있습니다.\n\n그런 장점이 있는 언어를 이용하여 게임 개발을 해는 것이 좋다고 생각하였습니다.\n\n따라서 파이썬에서 게임 개발을 지원해주는 PyGame을 이용하기로 결정하였습니다.\n\n\u003cb\u003ePyGame 1.9.6버전을 사용했습니다.\u003c/b\u003e\n\n![엔진](https://www.pygame.org/images/logo_lofi.png)\n\n**2. 왜 PyGame 프레임 워크를 개발하는가?**\n\nPyGame은 게임 엔진이 아니라 기능을 지원해주는 모듈입니다.\n\n따라서 게임 개발을 하면 PyGame의 기능을 반복적으로 사용하는 일이 있습니다.\n\n반복 되는 행동과 코드의 양을 동시에 줄이기 위해 함수화하고\n\n그 함수들을 클래스로 묶는 것이 개발 하는데 최적화 방법이므로\n\n프레임워크를 개발해보는 것이 좋다고 생각하였습니다.\n\n[DL-Engine-PyGame](https://github.com/DaLae37/DL-Engine)\n\n**3. 왜 물리와 삼각함수를 게임에 적용시켜보는가?**\n\n고등학교 때부터 게임을 만들기 위해서는 물리와 수학이 중요하다고 들어왔습니다.\n\n대학교에 진학하여 물리와 미적분을 배우면서 게임에 응용할만한 내용이 많다고 생각이 들었습니다.\n\n게임에서 물리와 수학이 어떻게 쓰일 수 있는지 경험해보고자 물리와 수학을 게임에 적용시켜보는 것을 주제로 삼았습니다.\n\n또한 저는 게임콘텐츠 트랙을 희망하고 있어 게임 개발 프로젝트를 진행해보는 것이 좋다고 생각하였습니다.\n\n### 3.기획\n\n씬은 mainScene, gameScene, rankingScene 세 가지 씬이 존재합니다.\n\n플레이어와 공, 데이터들은 singleton class로 구현할 것입니다.\n\n게임이 시작되면 공을 띄우고 그 공을 때릴 수 있습니다.\n\n단, 친 공은 물이 아닌 땅에 떨어져야합니다.\n\n이런 조건을 지키기 위해 플레이어가 공에 가하는 힘과 각도를 조절 할 필요가 있습니다.\n\n그 조건이 이 게임에서 가장 중요한 요소입니다.\n\n플레이어가 친 공이 땅에 떨어지면 point가 +1이 되며 그 위치에서 한 번 더 타격을 할 수있습니다.\n\n이런 동작을 물에 떨어질 때까지나 목적지에 다다를 때까지 반복합니다.\n\n결과에 따라 보상을 받을 것이며\n\n그 보상(재화)으로 플레이어가 가할 수 있는 힘의 최대치를 증가한다던지,\n\n공의 탄성도를 증가시킨다던지 등 게임의 요소를 구매할 수 있습니다.\n\n미적분 시간에 배웠던 쌍곡선 삼각함수를 이용하여 바다 파도를 구현할 것입니다.\n\n또 물리 시간에 배웠던 포물선 운동을 이용하여 공의 움직임을 구현할 것입니다.\n\n메인 화면\n![프로토타입1](https://www.dalae37.com/project/hitaball/resource/image/hitaball_prototype1.webp)\n\n게임 화면\n![프로토타입2](https://www.dalae37.com/project/hitaball/resource/image/hitaball_prototype2.webp)\n![프로토타입3](https://www.dalae37.com/project/hitaball/resource/image/hitaball_prototype3.webp)\n\n## Report\n\n### 4.Framework\n\n뼈대 공사먼저 하여 건물을 튼튼하게 짓는 것 처럼\n\n프레임워크를 작성하여 게임 개발을 용이하게 하도록 하였습니다\n\npygame은 기본적인 프레임 워크를 지원하지 않습니다.\n\n따라서 개발 결과물을 보면 한 파일에 \n\n사운드 기능과 씬 전환, 카메라 기능을 모두 넣어야하는 경우가 많습니다.\n\n그럴 경우 한 파일의 코드 수가 어마어마하게 늘어나는 문제가 있습니다.\n\n저는 이 문제를 해결하기 위해\n\n프레임워크를 작성하고 개발하였습니다.\n\n아래는 애니메이션 기능을 구현한 animaton.py 파일입니다.\n\npygame은 애니메이션을 직접적으로 지원하지 않기 때문에 이미지의 반복으로 애니메이션을 구현하였습니다.\n\n각각의 Sprite를 리스트에 저장한 후 그 Sprite를\n\n1초에 정해진 frame 수만큼 index를 반복합니다.\n\n예) 10frame -\u003e 1초에 10개의 Sprite 출력\n\n\n```python\nimport pygame\nimport time\nfrom pygame import Surface\nfrom pygame.color import Color\nfrom pygame.sprite import Sprite\n\nclass Animation(Sprite) :\n    def __init__(self, images, frames):\n        Sprite.__init__(self)\n        self.clock = pygame.time.Clock()\n\n        self.images = list()\n        self.frames = frames\n        self.beforeTime = 0\n\n        for i in images :\n            self.images.append(pygame.image.load(i))\n\n        self.image_count = len(images)\n        self.current_frame = 0\n        self.image = self.images[self.current_frame]\n\n        self.rect = pygame.Rect(0, 0, self.image.convert().get_width(), self.image.convert().get_height())\n\n    def update(self):\n        if time.time() - self.beforeTime \u003e 1 / self.frames :\n            if self.current_frame is self.image_count -1 :\n                self.current_frame = 0\n            else:\n                self.current_frame += 1\n            self.image = self.images[self.current_frame]\n            self.beforeTime = time.time()\n\n    def setPos(self, pos) :\n        self.rect.x = pos[0]\n        self.rect.y = pos[1]\n\n    def addPos(self, pos) :\n        self.rect.x += pos[0]\n        self.rect.y += pos[1]\n\n    def getPos(self) :\n        return (self.rect.x, self.rect.y)\n\n    def getSize(self) :\n        return self.image.get_size()\n\n    def getSurface(self) :\n        return self.image\n\n    def isCollisionRect(self, pos) :\n        left_x = self.getPos()[0]\n        left_y = self.getPos()[1]\n        right_x = self.getPos()[0] + self.getSize()[0]\n        right_y = self.getPos()[1] + self.getSize()[1]\n\n        return left_x \u003c= pos[0] and left_y \u003c= pos[1] and right_x \u003e= pos[0] and right_y \u003e= pos[1]\n\n    def getTag(self) :\n        return \"Animation\"\n```\n\n    pygame 1.9.6\n    Hello from the pygame community. https://www.pygame.org/contribute.html\n    \n\n아래는 씬 전환 기능을 구현한 sceneManager.py 파일입니다.\n\n파일에는 Scene과 SceneManager 이 두 가지 클래스가 있습니다.\n\n게임 내의 모든 씬은 Scene클래스를 상속받으며 60프레임으로 반복되는 함수 update에서 이미지 랜더와 오브젝트 동작이 동시에 이뤄집니다.\n\n\n```python\nclass Scene() :\n    def update(self) :\n        pass\n    def load_resources(self) :\n        pass\n    def ui_event(self) :\n        pass\n\nclass SceneManager(Scene) :\n    instance = None\n    point = 0\n    \n    def __init__(self) :\n        self.isQuit = False\n        self.current_scene = None\n\n    @classmethod\n    def getInstance(cls) :\n        if cls.instance is None :\n            cls.instance = SceneManager()\n        return cls.instance\n\n    def changeScene(self, replaced_scene) :\n        self.current_scene = replaced_scene\n\n    def update(self) :\n        self.current_scene.update()\n\n    def setQuit(self, isQuit) :\n        self.isQuit = isQuit\n\n    def setPoint(self, point) :\n        self.point = point\n\n    def getPoint(self) :\n        return self.point\n```\n\n아래는 simple_image.py 파일입니다.\n\nSimpleImage 클래스는 단순한 배경화면, UI 등 pygame의 Sprite클래스의 모든 기능이 필요하지 않은 Image를 위해 구현하였습니다.\n\n\n```python\nimport pygame\n\nclass SimpleImage() :\n\n    centerMode = False\n\n    def __init__(self, directory) :\n        self.image = pygame.image.load(directory)\n        self.rect = self.image.get_rect()\n        self.centerMode = False\n\n    def getInfo(self) :\n        return (self.image, self.rect)\n\n    def setPos(self, pos) :\n        self.rect.x = pos[0] - ((self.getSize()[0] // 2) if self.centerMode else 0)\n        self.rect.y = pos[1] - ((self.getSize()[1] // 2) if self.centerMode else 0)\n\n    def addPos(self, pos) :\n        self.rect.x += pos[0]\n        self.rect.y += pos[1]\n\n    def getPos(self) :\n        return (self.rect.x, self.rect.y)\n\n    def setSize(self, size) :\n        if self.centerMode :\n            self.addPos((self.getSize()[0] // 2, self.getSize()[1] // 2))\n        self.image = pygame.transform.scale(self.image,size)\n        self.setPos(self.getPos())\n\n    def getSize(self) :\n        return self.image.get_size()\n\n    def setCenterMode(self, centerMode) :\n        self.centerMode = centerMode\n\n    def getSurface(self) :\n        return self.image\n\n    def setSurface(self, image) :\n        self.image = image\n\n    def isCollisionRect(self, pos) :\n        left_x = self.getPos()[0]\n        left_y = self.getPos()[1]\n        right_x = self.getPos()[0] + self.getSize()[0]\n        right_y = self.getPos()[1] + self.getSize()[1]\n\n        return left_x \u003c= pos[0] and left_y \u003c= pos[1] and right_x \u003e= pos[0] and right_y \u003e= pos[1]\n\n    def getTag(self) :\n        return \"SimpleImage\"\n```\n\n아래는 사운드 기능을 구현한 soundManager.py 파일입니다.\n\n배경음악은 music\n\n효과음 등 단발성 사운드는 sound로 구분하여 사용합니다.\n\n효과음은 한 번 불러온 뒤 반복적으로 실행하는 경우가 있으므로\n\n딕셔너리에 \"사운드 이름\" : 사운드 객체 로 저장하여 사용합니다.\n\n\n```python\nimport pygame\nimport os, sys\nsys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))\n\nclass SoundManager() :\n    instance = None\n\n    def __init__(self) :\n        self.sound_dict = dict()\n\n    @classmethod\n    def getInstance(cls) :\n        if cls.instance is None :\n            cls.instance = SoundManager()\n        return cls.instance\n\n    def load_music(self, directory) :\n        self.stop_music()\n\n        pygame.mixer.music.load(directory)\n        pygame.mixer.music.play(-1)\n\n    def stop_music(self) :\n        if pygame.mixer.get_busy() :\n            pygame.mixer.music.stop()\n\n    def load_sound(self, directory, sound_name) :\n        sound = pygame.mixer.Sound(directory)\n        self.sound_dict[sound_name] = sound\n\n    def play_sound(self, sound_name) :\n        self.sound_dict[sound_name].play()\n```\n\n아래는 cameraManager.py 파일입니다.\n\n오브젝트 추적 기능이 있으며\n\n모든 오브젝트를 랜더할 때 위치를 카메라와 상대 위치를 구해서 랜더 하여 카메라 기능을 구현하였습니다.\n\n\n```python\nimport pygame\nimport os, sys\nsys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))\n\nclass CameraManager() :\n    isFollowing = False\n    followedObject = None\n    instance = None\n    cameraX = 0\n    cameraY = 0\n    dX = 0\n    dy = 0\n    def update(self) :\n        if self.isFollowing and self.followedObject.getIsMove() is True :\n            x = self.followedObject.getPos()[0]\n            y = self.followedObject.getPos()[1]\n            self.addCameraPos((x - self.dX, y - self.dY))\n            self.dX = x\n            self.dY = y\n            if self.getCameraPos()[1] \u003e 0 :\n                self.setCameraPos((self.getCameraPos()[0],0))\n\n    @classmethod\n    def getInstance(cls) :\n        if cls.instance is None :\n            cls.instance = CameraManager()\n        return cls.instance\n\n    def setCameraPos(self, pos) :\n        self.cameraX = pos[0]\n        self.cameraY = pos[1]\n\n    def addCameraPos(self, pos) :\n        self.cameraX += pos[0]\n        self.cameraY += pos[1]\n\n    def getCameraPos(self) :\n        return (self.cameraX, self.cameraY)\n\n    def setFollowedObject(self, obj) :\n        self.followedObject = obj\n        x = self.followedObject.getPos()[0]\n        y = self.followedObject.getPos()[1]\n        self.dX = x\n        self.dY = y\n        self.isFollowing = True\n\n    def releaseObject(self) :\n        self.followedObject = None\n        self.isFollowing = False\n```\n\n## 5.Objects\n\n\n\n\n```python\nfrom Framework.simple_image import SimpleImage\nimport pygame\nimport math\nimport os, sys\nsys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))\n\nclass Ball(SimpleImage) :\n    instance = None\n    def __init__(self) :\n        super().__init__(\"Resources/Images/Ball/Ball.png\")\n        self.isMove = False\n        self.isStart = False\n        self.t = 0\n        self.power = 50\n        self.beforeT = 0\n        self.degree = 0\n\n    def update(self) :\n\n        if self.isStart :\n            if self.isMove :\n                self.addPos((-self.power * math.cos(self.degree * math.pi / 180), -self.power * math.sin(self.degree * math.pi / 180) + 9.8 * (self.t - self.beforeT)))\n        else :\n            self.addPos((0,0.5 * 9.8 * (self.t * self.t)))\n\n        self.t+=1\n\n    def setIsMove(self, isMove) :\n        if self.isMove is False and isMove is True :\n            self.beforeT = self.t\n        self.isMove = isMove\n\n    def getIsMove(self) :\n        return self.isMove\n\n    def setIsStart(self, isStart) :\n        if self.isStart is True :\n            self.t = 0\n        self.isStart = isStart\n\n    def getIsStart(self) :\n        return self.isStart\n\n    def setPower(self, power) :\n        self.power = power\n\n    def setDegree(self, degree) :\n        self.degree = degree\n```\n\n\n\n\n```python\nfrom Framework.simple_image import SimpleImage\nimport os, sys\nsys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))\n\nclass OverGround(SimpleImage) :\n    def __init__(self) :\n        super().__init__(\"Resources/Images/Ground/overGround.png\")\n\n    def whereCollide(self, movedObject) :\n        mX = movedObject.getPos()[0]\n        mY = movedObject.getPos()[1]\n        mSY = movedObject.getSize()[1]\n\n        if mY \u003c 620:\n            return 0\n        else :\n            return 1\n\n    def getTag(self) :\n        return \"Ground\"\nclass UnderGround(SimpleImage) :\n    def __init__(self) :\n        super().__init__(\"Resources/Images/Ground/underGround.png\")\n\n    def whereCollide(self, movedObject) :\n        return 1\n\n    def getTag(self) :\n        return \"Ground\"\n```\n\n\n\n### 6.Codes\n\nmain.py\n\n\n```python\nimport pygame\n\nfrom Framework.sceneManager import SceneManager\nfrom mainScene import mainScene\n\nif __name__ == '__main__' :\n    pygame.init()\n    pygame.mixer.pre_init(44100, 16, 2, 4096) #Frequency, Size, Channels, BufferSize\n\n    screen = pygame.display.set_mode((1280, 720))\n    pygame.display.set_caption(\"Hit A Ball\")\n    clock = pygame.time.Clock()\n    run = True\n\n    SceneManager.getInstance().changeScene(mainScene(screen,clock))\n\n    while run:\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                run = False\n                pass\n\n        if SceneManager.getInstance().isQuit :\n            run = False\n\n        SceneManager.getInstance().update()\n        pygame.display.flip()\n        clock.tick(60)\n\n    pygame.quit()\n\n```\n\n\n```python\nimport pygame\nfrom Framework.sceneManager import Scene, SceneManager\nfrom Framework.soundManager import SoundManager\nfrom Framework.animation import Animation\nfrom Framework.simple_image import SimpleImage\n\nclass mainScene(Scene) :\n\n    simple_image_list = []\n\n    def __init__(self, screen, clock) :\n        self.screen = screen\n        self.clock = clock\n\n        self.load_resources()\n\n    def update(self) :\n\n        for si in self.simple_image_list :\n            self.screen.blit(si.getSurface(), si.getPos())\n\n        self.screen.blit(self.title, (350,100))\n\n        for event in pygame.event.get() :\n            if event.type == pygame.QUIT :\n                SceneManager.getInstance().isQuit = True\n                return\n\n            if event.type == pygame.MOUSEBUTTONDOWN :\n                if self.startButton.isCollisionRect(pygame.mouse.get_pos()) :\n                    from gameScene import gameScene\n                    SceneManager.getInstance().changeScene(gameScene(self.screen, self.clock))\n                    return\n\n                if self.exitButton.isCollisionRect(pygame.mouse.get_pos()) :\n                    SceneManager.getInstance().isQuit = True\n                    return\n\n\n    def load_resources(self) :\n        SoundManager.getInstance().load_music(\"Resources/Sounds/BGM.mp3\")\n\n        self.background = SimpleImage(\"Resources/Images/Background/mainScene.png\")\n        self.background.setPos((0,0))\n        self.background.setSize((1280,720))\n        self.simple_image_list.append(self.background)\n\n        self.startButton = SimpleImage(\"Resources/Images/UI/Start.png\")\n        self.startButton.setCenterMode(True)\n        self.startButton.setPos((640,400))\n        self.startButton.setSize((300, 100))\n        self.simple_image_list.append(self.startButton)\n\n        self.exitButton = SimpleImage(\"Resources/Images/UI/Exit.png\")\n        self.exitButton.setCenterMode(True)\n        self.exitButton.setPos((640, 550))\n        self.exitButton.setSize((300, 100))\n        self.simple_image_list.append(self.exitButton)\n\n\n        self.title = pygame.font.SysFont(\"Monospace\", 100).render(\"Hit A Ball\", True, (0,0,0))\n```\n\n\n```python\nimport pygame\nimport math\nimport random\nfrom Objects.ball import Ball\nfrom Objects.ground import UnderGround, OverGround\nfrom Framework.sceneManager import Scene, SceneManager\nfrom Framework.animation import Animation\nfrom Framework.simple_image import SimpleImage\nfrom Framework.cameraManager import CameraManager\n\nclass gameScene(Scene) :\n\n    objectList = []\n    uiList = [] #barPos = ui[0] barSize = ui[1] borderColor = ui[2] barColor = ui[3]\n\n    def __init__(self, screen, clock) :\n        self.screen = screen\n        self.clock = clock\n\n        self.isMove = False\n        self.powerOn = False\n        self.power = 0\n        self.degreeOn = False\n        self.degree = 0\n\n        self.t = 0\n        self.waterAnim = list()\n\n        self.beforeCamX = 0\n        self.beforeCamY = 0\n\n        self.load_resources()\n\n    def update(self) :\n        CameraManager.getInstance().update()\n        self.screen.fill(pygame.Color('white'))\n        self.ball.update()\n\n        camX = CameraManager.getInstance().getCameraPos()[0]\n        camY = CameraManager.getInstance().getCameraPos()[1]\n        for si in self.objectList :\n            siX = si.getPos()[0]\n            siY = si.getPos()[1]\n\n            if siX \u003e 2500 : #카메라에서 벗어난 객체는 삭제\n                self.objectList.remove(si)\n\n            else : #벗어나지 않은 객체는 카메라와의 거리를 고려해 Render\n                if not (CameraManager.getInstance().followedObject is si) :\n                    si.setPos((siX -(camX - self.beforeCamX) ,siY - (camY - self.beforeCamY)))\n                self.screen.blit(si.getSurface(), si.getPos())\n\n            if si.getTag() is \"Animation\" :\n                si.update()\n\n            if si.isCollisionRect(self.ball.getPos()) :\n                if si.getTag() is \"Ground\" :\n                    if si.whereCollide(self.ball) is 0 :\n                        if self.ball.getIsStart() is False :\n                            self.ball.setIsStart(True)\n                        if self.ball.getIsMove() is True :\n                            self.ball.setIsMove(False)\n                            SceneManager.getInstance().setPoint(SceneManager.getInstance().getPoint() + 1)\n                        self.ball.setPos((self.ball.getPos()[0], si.getPos()[1] - self.ball.getSize()[1]))\n\n                    else :\n                        self.ball.setIsStart(False)\n                        self.ball.setPos((si.getPos()[0] + si.getSize()[0], self.ball.getPos()[1]))\n\n                elif si.getTag() is \"Animation\" :\n                    CameraManager.getInstance().releaseObject()\n                    CameraManager.getInstance().setCameraPos((0,0))\n                    from resultScene import resultScene\n                    SceneManager.getInstance().changeScene(resultScene(self.screen,self.clock))\n                    return\n        self.beforeCamX = camX\n        self.beforeCamY = camY\n\n        for ui in self.uiList :\n            barPos = ui[1]\n            barSize = ui[2]\n            borderColor = ui[3]\n            barColor = ui[4]\n            if ui[0] is \"power\" :\n                gaze = (self.power * 14 % 350) / 350\n            elif ui[0] is \"degree\" :\n                gaze = (self.degree * 11.9 % 350) / 350\n            self.DrawBar(barPos, barSize, borderColor, barColor, gaze)\n\n        if self.powerOn :\n            self.power+=1\n            if self.power \u003e 25 : #power의 증가량은 최대 25까지\n                self.power = 0\n\n        if self.degreeOn :\n            self.degree+=1\n            if self.degree \u003e 30 : #degree의 증가량은 최대 30까지\n                self.degree = 0\n\n        for event in pygame.event.get() :\n            if event.type == pygame.QUIT :\n                SceneManager.getInstance().isQuit = True\n                return\n\n            if event.type == pygame.MOUSEBUTTONDOWN :\n                self.degreeOn = True\n\n            if event.type == pygame.MOUSEBUTTONUP :\n                self.degreeOn = False\n                self.ball.setDegree(self.degree + 30)\n\n            if event.type == pygame.KEYDOWN :\n                if event.key == pygame.K_SPACE :\n                    self.powerOn = True\n                if event.key == pygame.K_LEFT :\n                    for si in self.objectList :\n                        si.addPos((10, 0))\n                if event.key == pygame.K_RIGHT :\n                    for si in self.objectList :\n                        si.addPos((-10, 0))\n\n            if event.type == pygame.KEYUP :\n                if event.key == pygame.K_SPACE :\n                    self.powerOn = False\n                    self.ball.setPower(self.power + 50)\n\n                if event.key == pygame.K_RETURN :\n                    self.ball.setIsMove(True)\n\n    def load_resources(self) :\n        for i in range(10) :\n            underBackground = SimpleImage(\"Resources/Images/Background/underBackground.png\")\n            underBackground.setPos((0 - 1280 * i,0))\n            self.objectList.append(underBackground)\n\n            background = SimpleImage(\"Resources/Images/Background/background.png\")\n            background.setPos((0 - 1280 * i,-500))\n            self.objectList.append(background)\n\n        water_list = list()\n        for i in range(8) :\n            water_list.append('Resources/Images/Water/'+str(i+1)+'.png')\n\n        tmpCount = 0\n        for i in range(100) :\n            tmp = random.randrange(0, 5) + 1\n            if i \u003c 2 or tmpCout \u003e 4:\n                tmp = 1\n\n            if tmp \u003e= 2 :\n                water = Animation(water_list,8)\n                water.setPos((1180 - 100 * i ,620))\n                self.objectList.append(water)\n                tmpCout += 1\n            else :\n                overGround = OverGround()\n                overGround.setPos((1180 - 100 * i, 520))\n                self.objectList.append(overGround)\n\n                underGround = UnderGround()\n                underGround.setPos((1180 - 100 * i, 620))\n                self.objectList.append(underGround)\n                tmpCout = 0\n\n        self.ball = Ball()\n        self.ball.setPos((1200, 300))\n        self.objectList.append(self.ball)\n        CameraManager().getInstance().setFollowedObject(self.ball)\n\n        self.uiList.append([\"power\",(1000, 50),(200, 20),(0, 0, 0),(0, 128, 0)])\n        self.uiList.append([\"degree\",(1000, 100),(200, 20),(0, 0, 0),(128, 0, 0)])\n\n    def DrawBar(self, pos, size, borderC, barC, progress):\n        pygame.draw.rect(self.screen, borderC, (*pos, *size), 1)\n        innerPos  = (pos[0]+3, pos[1]+3)\n        innerSize = ((size[0]-6) * progress, size[1]-6)\n        pygame.draw.rect(self.screen, barC, (*innerPos, *innerSize))\n```\n\n\n```python\nimport pygame\nfrom typing import TYPE_CHECKING\nfrom Framework.sceneManager import Scene, SceneManager\nfrom Framework.soundManager import SoundManager\nfrom Framework.animation import Animation\nfrom Framework.simple_image import SimpleImage\n\nclass resultScene(Scene) :\n\n    simple_image_list = []\n\n    def __init__(self, screen, clock) :\n        self.screen = screen\n        self.clock = clock\n\n        self.load_resources()\n\n    def update(self) :\n\n        for si in self.simple_image_list :\n            self.screen.blit(si.getSurface(), si.getPos())\n\n        self.screen.blit(self.title, (350,100))\n\n        for event in pygame.event.get() :\n            if event.type == pygame.QUIT :\n                SceneManager.getInstance().isQuit = True\n                return\n\n            if event.type == pygame.MOUSEBUTTONDOWN :\n                if self.startButton.isCollisionRect(pygame.mouse.get_pos()) :\n                    from mainScene import mainScene\n                    SceneManager.getInstance().changeScene(mainScene(self.screen, self.clock))\n                    return\n\n                if self.exitButton.isCollisionRect(pygame.mouse.get_pos()) :\n                    SceneManager.getInstance().isQuit = True\n                    return\n\n\n    def load_resources(self) :\n        self.background = SimpleImage(\"Resources/Images/Background/mainScene.png\")\n        self.background.setPos((0,0))\n        self.background.setSize((1280,720))\n        self.simple_image_list.append(self.background)\n\n        self.startButton = SimpleImage(\"Resources/Images/UI/Start.png\")\n        self.startButton.setCenterMode(True)\n        self.startButton.setPos((640,400))\n        self.startButton.setSize((300, 100))\n        self.simple_image_list.append(self.startButton)\n\n        self.exitButton = SimpleImage(\"Resources/Images/UI/Exit.png\")\n        self.exitButton.setCenterMode(True)\n        self.exitButton.setPos((640, 550))\n        self.exitButton.setSize((300, 100))\n        self.simple_image_list.append(self.exitButton)\n\n\n        self.title = pygame.font.SysFont(\"Monospace\", 100).render(\"Score : \" + str(SceneManager.getInstance().getPoint()), True, (0,0,0))\n```\n\n## 7.Result\n\n![메인](https://www.dalae37.com/project/hitaball/resource/image/hitaball.webp)\n\n![인게임1](https://www.dalae37.com/project/hitaball/resource/image/hitaball_ingame1.webp)\n\n![인게임2](https://www.dalae37.com/project/hitaball/resource/image/hitaball_ingame2.webp)\n\"SpaceBar\"를 누르고 있으면 공을 발사할 힘 게이지가 증가합니다\n\n![인게임3](https://www.dalae37.com/project/hitaball/resource/image/hitaball_ingame3.webp)\n\"MouseClick\"을 누르고 있으면 공을 발사할 각도 게이지가 증가합니다\n\n![인게임4](https://www.dalae37.com/project/hitaball/resource/image/hitaball_ingame4.webp)\n자신이 원하는 만큼 게이지를 충전하고 \"Enter\"를 누르면 공이 발사가 됩니다\n\n![인게임5](https://www.dalae37.com/project/hitaball/resource/image/hitaball_ingame5.webp)\n\n![인게임6](https://www.dalae37.com/project/hitaball/resource/image/hitaball_ingame6.webp)\n\n![엔딩](https://www.dalae37.com/project/hitaball/resource/image/hitaball_ending.webp)\n\n[유튜브](https://youtu.be/PbmwTzbQRQ8)\n\n## 8.Github\n\n[Hit A Ball](https://github.com/DaLae37/HitABall)\n\n현재 프로젝트의 모든 소스 코드와 리소스는 위 링크에서 확인할 수 있습니다\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdalae37%2Fhitaball","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdalae37%2Fhitaball","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdalae37%2Fhitaball/lists"}