{"id":26151370,"url":"https://github.com/tomstillcoding/angryflappybird","last_synced_at":"2025-08-03T10:07:41.886Z","repository":{"id":37483603,"uuid":"504558685","full_name":"tomstillcoding/AngryFlappyBird","owner":"tomstillcoding","description":null,"archived":false,"fork":false,"pushed_at":"2022-08-24T14:55:52.000Z","size":1390,"stargazers_count":17,"open_issues_count":1,"forks_count":6,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-14T05:13:11.219Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tomstillcoding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-17T14:09:11.000Z","updated_at":"2025-04-12T03:41:30.000Z","dependencies_parsed_at":"2023-01-16T15:16:40.722Z","dependency_job_id":null,"html_url":"https://github.com/tomstillcoding/AngryFlappyBird","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tomstillcoding/AngryFlappyBird","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomstillcoding%2FAngryFlappyBird","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomstillcoding%2FAngryFlappyBird/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomstillcoding%2FAngryFlappyBird/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomstillcoding%2FAngryFlappyBird/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomstillcoding","download_url":"https://codeload.github.com/tomstillcoding/AngryFlappyBird/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomstillcoding%2FAngryFlappyBird/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268525529,"owners_count":24264209,"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","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2025-03-11T06:33:44.116Z","updated_at":"2025-08-03T10:07:41.834Z","avatar_url":"https://github.com/tomstillcoding.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FlappyAngryBird\n\n## 一、程序的功能说明\n\n本程序复刻了曾经风靡一时的魔性小游戏，实现了移动、跳跃、碰撞判定、计 分等一系列游戏功能\n\n\u003cimg src=\"./assets/34-angrybirds.assets/Screen Shot 2022-06-16 at 00.36.16.png\" alt=\"Screen Shot 2022-06-16 at 00.36.16\" style=\"zoom:40%;\" /\u003e\n\n\u003cimg src=\"./assets/34-angrybirds.assets/Screen Shot 2022-06-16 at 00.36.39.png\" alt=\"Screen Shot 2022-06-16 at 00.36.39\" style=\"zoom:30%;\" /\u003e\n\n## 二、程序的算法和开发部分 \n\n### 1、程序的算法\n\n不断更新小鸟和水管的位置和状态，并不断在面板上重新绘制，以达到运动的效果。\n\n其中，为了小鸟的飞行轨迹看起来是非常顺滑的抛物线，因此需要转动小鸟，Bird 类中控制小鸟旋转的 getDrawer 方法中我们建立了如下的模型: 假设小鸟的速度为 v，则小鸟在水平方向的偏转角 θ(单位为度) 为：\n\n\u003cimg src=\"./assets/34-angrybirds.assets/Screen Shot 2022-06-16 at 00.37.59.png\" alt=\"Screen Shot 2022-06-16 at 00.37.59\" style=\"zoom: 50%;\" /\u003e\n\n但是在向下旋转时有可能会出现速度过大而使 θ \u003e 90 的情况，因此在 θ \u003e 90 时应将 θ 固定在 90 度，即 π/2 弧度，因此有了 getDrawer 方法中的算法。\n\n### 2、数据结构和面向对象实现\n\n- 总览\n  - 程序共 8 个类，分别是 Main,MainGame,Panel,Drawer,Loader,Keybord,Bird,Tube. 其中 Panel 类继承了 JPanel 类，实现了 Runnable 接口，Keyborad 类实现了 KeyListener 接口。\n- Drawer：包括 int 型属性 x 和 y，Image 型的属性 image 和 AffineTransform 型的属性 transform，其中 x 和 y 代表图片的位置，而 image 代表的是图片对象， 这个时候就需要新建的类 ImageLoader 来返回 Image 对象到 Drawer 中，最后一 个属性 transform 代表图像的变换形式，在小鸟的飞行过程中，它的旋转就会用到 transform。\n- ImageLoader：由于我们不希望每次读取图片地址时都要进行 IO 操作来读写 文件，所以经过翻阅网上的资料，我们找到了利用 hashmap 实现对图片地址的缓 存，即可以用户查到数据，查询条件相同时，第一次走数据库，第二次以后在一段 时间内走缓存，提高响应的速度。\n- Tube：类表示管道，它包含的属性包括 int 型属性 x, y width, height, speed; String 属性 direction，Image 属性 image。reset() 能够重置管道，将管道位置放在窗口右边显示部分之外，并且能够将 South 管道所在位置用 Math.random() 初始 化;update() 可以让管道移动向左按 speed 速度移动，collides() 方法可以对小鸟进 行碰撞检测，如果小鸟图片各个位置的坐标与 tube 有接触，那么就代表碰撞，即 游戏结束。\n- Keyboard：我们将键盘的响应设计成改变键值对应的 boolean 类型数组下标对 应的值，来获得用户的反馈，我们建立了 keys[] 数组，其中包括了键值 P、R、 SPACE 的对应 boolean 类型值，每次 update 进行更新游戏每帧各个类的参数的时 候，我们通过 Override keyPressed() 和 keyReleased() 方法来监视键值 keys[] 的改 变，来判断是否 SPACE、R、P 是否被按下，来实现跳跃、重新开始、暂停这样 的简单功能。\n- Panel：此类即是代表窗口对象，继承 JPnel，实现 Runnable 接口，新建线程， 实例化 MainGame 类，通过不停 update() 各个对象的各种属性，来将对应不同时 刻时的图像都 draw 进窗口中，而通过控制线程 Thread.sleep(30)，和管道、小鸟下 坠和飞行速度，来达到同步画面。\n\n## 三、其他说明 \n\n### 1、画面表现\n\n- 控制线程休眠时间达到控制连续刷新画面的频率，使窗口不停绘制图像，达到 连续播放的画面。\n- 通过使用 HashMap 缓存图片，降低读写文件次数，提升程序性能。\n- 通过设计 boolean keys[] 同时判断多个按键是否被按下。利用 AffineTransform 实现简单的 2 维空间的坐标仿射变换来实现让图片旋转的目的，使鸟飞行方式显得更自然不僵硬。\n- 通过设置 Delay 属性避免在快速连续按键时的键盘监听 bug。\n\n### 2、特殊问题\n\n- 如何实现动画的连续播放？我们在设计动画程序的时候(非 GUI 程序)遇到 了如下两个较为棘手的问题。\n  - 第一个问题，如何实现动画的动态属性。采用的 是不停访问 Panel 类的 update() 方法，更新数值并 repaint() 从而不停地重画 该界面，实现我们能看到的动的效果。\n  - 第二个问题，动画过快或者过慢，或者 出现屏闪。通过查与资料发现主要是和线程的睡眠时间有关，睡眠时间越短， 鸟儿速度失控就越严重并且会出现屏闪。解决办法就是提前将所有画面的属性 都改好，再一次性绘制在屏幕上，所以我们使用了 Drawer 类来统一所有图片 对象，先把所有东西的下一帧出现的位置都设置好，然后一次性把画好的内容 显示出来。\n- 如何使小鸟飞行时有旋转的动作？在 Drawer 中添加 AffineTransform 型的属 性 transform，代表图像的变换形式。\n- 如何同时监听多个键盘按键？建立 keys[256] 数组，其中包括键值 P、R、 SPACE 的对应 boolean 类型值，每次 update 进行更新游戏每帧各个类的参数 的时候，通过 Override keyPressed() 和 keyReleased() 方法来监视键值 keys[] 的改变，来判断是否 SPACE、R、P 是否被按下，来实现跳跃、重新开始、暂 停这样的简单功能。\n\n## 四、参考资料\n\n1、关于 paint(),repaint(),paintcomponent() https://blog.csdn.net/zi_jun/article/details/7624732\n\n2、JPanel中使用paintComponent()方法绘图时，调用repaint()为何不能刷新窗口? https://bbs.csdn.net/topics/340015900\n\n3、Graphics2D使用详解[转] https://blog.csdn.net/qq_32786873/article/details/71405933\n\n4、JFrame(框架)中添加和设置JPanel(面板)的方法 https://blog.csdn.net/lyxaiclr/article/details/7366145/\n\n5、关于 KeyListener 的简单使用 https://blog.csdn.net/u010142437/article/details/8914773\n\n6、javakeypressed长按的时候会有一小段的延迟 https://zhidao.baidu.com/question/744767566807027372.html\n\n7、HashMap实现简单的缓存Cache https://blog.csdn.net/u012260707/article/details/50592893\n\n8、使用 HashMap 做一个缓存案例 [缓存一张图片] https://blog.csdn.net/zhyzh134/article/details/50754854\n\n9、解决 Java 设计游戏的时候，画面不动和闪烁问题，双缓冲 https://blog.csdn.net/utnewbear/article/details/8190817\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomstillcoding%2Fangryflappybird","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomstillcoding%2Fangryflappybird","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomstillcoding%2Fangryflappybird/lists"}