爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      首页 知识中心 云端实践 文章详情页

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      2025-04-09 09:15:47 阅读次数:8

      动画,图像,屏幕,方法,游戏,绘制

      第4章 Java游戏程序的基本框架

      4.1 动画的类型及帧频

      4.1.1 动画类型

      动画分为影视动画和游戏动画两种。

      游戏动画是在屏幕上显示一系列连续动画画图的第一帧图形,然后在每隔很短时间显示下一帧图像,如此反复,利用人眼的视觉暂留现象而感觉好像画面的物体在运动。

      也就是说游戏动画就是图像在很短时间间隔内连续显示。

      4.1.2 设置合理的帧频

      FPS就是每秒钟的帧数,即每秒显示多少张图像,每一帧就是一幅静态图像,电影的播放速度是24FPS,游戏速度达到10FPS就能明显感觉到动画的效果了。

      但是屏幕上显示的图像越大,占用的内存越多,处理的速度就越慢,所以需要在显示大小和FPS间做一个权衡。

      4.2 游戏动画的制作

      4.2.1 绘制动画以及动画循环

      动画是一连串的图像快速循环播放,所以需要用到循环语句(while、for等)控制图像的连续播放。

      又由于动画需要一定的播放速度,所以需要连续播放动画的同时能够控制动画的播放速度,所以需要使用线程中的暂停函数(Thread.sleep())来控制。

      一般来说希望动画无限制地播放,示例代码如下:

      while(true){ // 死循环
          处理游戏功能;
          使用repaint()函数要求重画屏幕;
          暂停一小段时间;
      }

      在Java 游戏程序中, 通过repaint()函数请求屏幕的重画, 可以请求重画全部屏幕,也可以请求重画部分屏幕。

      下面以一个自由落体小球动画来演示:实现自由落体动画,首先设计一个自由降落的小球,同时控制降落的速度。控制速度就需要实现个继承了Runnable 线程接口和继承了JPanel类的TestPanel面板类。继承JPanel类是为了使用JPanel的Paint0方法来实现小球在屏幕上的绘制,继承Runnable线程接口可以实现动画的暂停控制。

      class TestPanel extends JPanel implements Runnable {
      
          public TestPanel() {
      
          }
      
          @Override
          public void paint(Graphics g) {
      
          }
      
          @Override
          public void run() {
      
          }
      }

      在Java中创建线程的方法有两种:继承Thread类和实现Runnable接口。这里演示的是实现Runnable接口,需要new Thread(Runnable runnable).start();来启动线程。也就是说要传入一个Runnable接口的实现类作为参数,来启动线程。

          public TestPanel() {
              // 创建一个新线程,this就是实现了Runnable接口的实现类
              Thread t = new Thread(this);
              // 启动线程
              t.start();
          }

      现在实现线程的run()方法,使用while(true)循环每隔30毫秒重新绘制动画场景,由于30毫秒很短,所以是连续的动画。这里采用Thread.sleep()方法来暂停30毫秒。

          @Override
          public void run() {
              while (true) {// 线程中的无限循环
                  try {
                      Thread.sleep(30);// 休眠30毫秒
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  ypos += 5;// 修改小球左上角的纵坐标
                  if (ypos > 300) {// 小球离开窗口后重设左上角的纵坐标
                      ypos = 80;
                  }
                  repaint();// 窗口重绘
              }
          }

      小球的重新绘制将在paint()方法中实现,通过不断更改小球要显示的y坐标,实现小球的自由落体,同时还要清除上一次(也就是30毫秒前)显示的小球,就可以看到看到小球的自由落体动画了。

          @Override
          public void paint(Graphics g) {
              g.clearRect(0, 0, this.getWidth(), this.getHeight());// 先清除屏幕上原来的画
              g.setColor(Color.GREEN);// 设置小球的颜色
              g.fillOval(0, ypos, 80, 80);// 绘制小球
          }

      clearRect()方法内是要清除的范围,前两个参数是x坐标和y坐标,后两个参数是宽度和高度,也就是一个矩形,这里是清理整个屏幕。

      小球的下落通过不断改变小球的显示y坐标来达到目的。该类的完整代码如下:

      class TestPanel extends JPanel implements Runnable {
          int ypos = 0;
      
          public TestPanel() {
              // 创建一个新线程,this就是实现了Runnable接口的实现类
              Thread t = new Thread(this);
              // 启动线程
              t.start();
          }
      
          @Override
          public void paint(Graphics g) {
              g.clearRect(0, 0, this.getWidth(), this.getHeight());// 先清除屏幕上原来的画
              g.setColor(Color.GREEN);// 设置小球的颜色
              g.fillOval(0, ypos, 80, 80);// 绘制小球
          }
      
          @Override
          public void run() {
              while (true) {// 线程中的无限循环
                  try {
                      Thread.sleep(30);// 休眠30毫秒
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  ypos += 5;// 修改小球左上角的纵坐标
                  if (ypos > 300) {// 小球离开窗口后重设左上角的纵坐标
                      ypos = 80;
                  }
                  repaint();// 窗口重绘
              }
          }
      }
      
      public class Demo {
          public static void main(String[] args) {
              JFrame frame = new JFrame();
              frame.setLocation(200, 200);
              frame.setSize(500, 500);
      
              TestPanel panel = new TestPanel();
              frame.setContentPane(panel);
      
              frame.setVisible(true);
              frame.setResizable(false);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }
      }

      效果如下图:

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      4.2.2 消除动画闪烁现象——双缓冲技术

      一个动画在运行的时候,如果图像的切换是在屏幕上完成的,则可能会造成屏幕的闪烁,消除动画闪烁现象的最佳方法是使用双缓冲技术。

      双缓冲技术在屏幕外做一个图像缓冲区, 事先在这个缓冲区内绘制图像,然后再将这个图像送到屏幕上,虽然动画中的图像切换很频繁,但是双缓冲技术很好地避免了在屏幕上进行消除和刷新时候的处理工作所带来的屏幕闪烁情况。但是在屏幕外的缓冲区需要占用一部分的内存资源,特别是图像比较大的时候,内存占用非常严重,因此-般需要考虑动画的质量和运行速度之间的重要性,有选择性地进行开发。

      1.屏幕产生闪烁的原因

      在Java游戏编程和动画编程中最常见的就是对于屏幕闪烁的处理。屏幕产生闪烁的原因是先用背景色覆盖组件再重绘图像的方式造成的。

      运行上面简单的小球下落动画程序后,我们会看到窗体中有一一个从上至下匀速运动的小球,但仔细观察,你会发现小球会不时地被白色的不规则横纹隔开,即所谓的屏幕闪烁,这不是我们预期的结果。

      Demo类的对象建立后,将显示窗口,程序首先自动调用重载后的paint(Graphics g)方法,在窗口上绘制一个小球,绘图线程启动后,该线程每隔30ms修改下小球的位置,然后调用repaint()方法。

      注意,这个repaint()方法是从JPanel类继承而来的。它先调用update(Graphics g)方法,update(Graphics g)方法再调用paint(Graphics g)方法。先用背景色覆盖掉整个组件,然后再调用paint(Graphics g)方法重新绘制小球,这样每次都是在一个新的位置看到一个小球,前面的小球被背景色覆盖了,实现了动画的效果,但是,就是这种方式导致了动画闪烁,在两次看到不同位置小球的中间时刻,总数存在一个在短时间内被绘制出来的空白画面(颜色取背景色)。另外,用paint(Graphics g)方法在屏幕上直接绘图的时候,由于执行的语句比较多,程序不断地改变窗体中正在被绘制的图像,会造成绘制的缓慢,这也从一-定程度上加剧了闪烁。

      闪烁效果视频如下(图片不能展示闪烁效果):

      swing动画闪烁效果

      使用双缓冲技术解决动画闪烁后的效果视频如下:

      使用双缓冲技术解决swing动画闪烁后的效果

      2.双缓冲技术

      所谓双缓冲,就是在内存中开辟一片区域, 作为后台图像,程序对它进行更新、修改,绘制完成后再显示到屏幕上。

      双缓冲技术的工作原理:先在内存中分配一个 与我们动画窗口-样大的空间( 在内存中的空间我们是看不到的),然后利用getGraphicsQ方法来获得双缓冲画笔,接着利用双缓冲画笔给空间描绘出我们想画的东西,最后将它全部一次性地显示到屏幕上。这样在我门的动画窗口上面显示出来就非常流畅了,避免了,上面的闪烁效果。

      3.双缓冲的使用

      一般采用重载paint(Graphics g)方法实现双缓冲,这种方式要求我们将双缓冲的处理放到paint(Graphics g)方法中。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      完整代码如下:

      class TestPanel extends JPanel implements Runnable {
          int ypos = 0;
          private Image iBuffer;
          private Graphics gBuffer;
      
          public TestPanel() {
              // 创建一个新线程,this就是实现了Runnable接口的实现类
              Thread t = new Thread(this);
              // 启动线程
              t.start();
          }
      
          @Override
          public void paint(Graphics g) {
              if (iBuffer == null) {
                  iBuffer = createImage(this.getSize().width, this.getSize().height);
                  gBuffer = iBuffer.getGraphics();
              }
              gBuffer.setColor(getBackground());
              gBuffer.fillRect(0, 0, this.getSize().width, this.getSize().height);
              gBuffer.setColor(Color.GREEN);
              gBuffer.fillOval(90, ypos, 80, 80);
              g.drawImage(iBuffer, 0, 0, this);
      //        g.clearRect(0, 0, this.getWidth(), this.getHeight());// 先清除屏幕上原来的画
      //        g.setColor(Color.GREEN);// 设置小球的颜色
      //        g.fillOval(0, ypos, 80, 80);// 绘制小球
          }
      
          @Override
          public void run() {
              while (true) {// 线程中的无限循环
                  try {
                      Thread.sleep(30);// 休眠30毫秒
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  ypos += 5;// 修改小球左上角的纵坐标
                  if (ypos > 300) {// 小球离开窗口后重设左上角的纵坐标
                      ypos = 80;
                  }
                  repaint();// 窗口重绘
              }
          }
      }
      
      public class Demo {
          public static void main(String[] args) {
              JFrame frame = new JFrame();
              frame.setLocation(200, 200);
              frame.setSize(500, 500);
      
              TestPanel panel = new TestPanel();
              frame.setContentPane(panel);
      
              frame.setVisible(true);
              frame.setResizable(false);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }
      }

      分析上述代码:我们首先添加了两个成员变量iBuffer和gBuffer 作为缓冲( 这就是所谓的双缓冲名称的来历)。在paint(Graphics g)方法中,首先检测如果iBuffer为null,则创建一个 与屏幕上的绘图区域大小一样的缓冲图像;再取得iBuffer的Graphics类型的对象的引用,并将其赋值给gBuffer;然后对gBuffer这个内存中的后台图像先用fllRect(int,int,int,int)清屏,再进行绘制操作,完成后将iBuffer直接绘制到屏幕上。

      这段代码看似可以完美地完成双缓冲,但是运行之后我们看到的还是严重的闪烁。什么原因呢?问题还是出现在update(Graphics g)方法上。这段修改后的程序中的update(Graphics g)方法还是我们从父类继承的。在update(Graphics g)方法中, clearRect(int,int,int,int)对 前端屏幕进行了清屏操作,而在paint(Graphics g)方法中,对后台图像又进行了清屏操作。那么如果保留后台清屏,去掉多余的前台清屏应该就会消除闪烁。因此,我们只要重载update(Graphics g)方法即可:

      public void update(Graphics g)
      {
          paint(g);
      }

      双缓冲技术的原理如下:

      • (1)定义一个Graphics对象gBuffer和一个Image对象iBuffer。 按屏幕大小建立一个缓冲对象给iBuffer。然后取得iBuffer的Graphics赋给gBuffer。 此处可以把gBuffer理解为逻辑上的缓冲屏幕,而把iBuffer理解为缓冲屏幕上的图像。
      • (2)在gBuffer (逻辑上的屏幕)上绘制图像。
      • (3)将后台图像iBuffer全部--次性地绘制到我们的前台窗口。

      注意,这个方法不是唯一解决闪烁的方法,在swing中,组件本身就提供了双缓冲功能,只需要进行方法调用就可以使用了,但是awt中没有提供。

      4.3 使用定时器

      定时器使用Timer组件,使用的是javax.swing.Timer包的Timer类实现,该类的构造方法是:Timer(int delay, ActionListener listener);。

      该构造方法用于建立一个Timer组件对象,参数listener用于指定一个接收该计时器操作事件的侦听器,指定所要触发的事件;而参数delay用于指定每一次触发事件的时间间隔,单位是毫秒。也就是说,Timer组件会根据用户所指定的delay时间,周期性地触发ActionEvent事件。如果要处理这个事件,就必须实现ActionListener接口类,以及接口类中的actionPerformed()方法。Timer组件类中的主要方法如下。

      • void start():激活Timer组件对象。
      • void stop():停止Timer组件对象。
      • void restart():重新激活Timer组件对象。

      下例每隔1000毫秒刷新一次时间。

          public static void main(String[] args) {
              JFrame frame = new JFrame();
              frame.setTitle("Timer测试");
              frame.setSize(300, 300);
       
              Container contentPane = frame.getContentPane();
              JLabel label = new JLabel("标签");
              contentPane.add(label);
       
              // 创建计时器组件Timer,传入两个参数,第一个参数是延迟1000毫秒,即每1000毫秒触发一次事件;第二个参数是事件处理
              Timer timer = new Timer(1000, new ActionListener() {
                  @Override
                  public void actionPerformed(ActionEvent e) {
                      SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                      Date date = new Date();
                      // 显示日期时间到JLabel标签中
                      label.setText(format.format(date));
                  }
              });
              timer.start();
       
              frame.setVisible(true);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      如果某个类实现了ActionListener 接口,因此可以直接设置timer = new Timer (500,this);使用this初始化计时器。当计时器启动后( timer.start()执行后),每隔500ms执行一次实现的ActionListener 接口中的actionPerformed的方法体。

      4.4 设置游戏难度

      如果使用速度控制游戏难度,则可以把游戏设计成为具有很多个级别,每个级别游戏的运行速度都不一样,类似的代码如下所示:

      public void level(){
          if(level==3){
              speed=1; //设置游戏速度为1
          }else{
              speed=2; //设置游戏速度为2
          }
      }

      4.5 游戏与玩家的交互

      游戏与玩家的交互都是通过键盘或鼠标实现的,例如通过上下左右键移动,通过鼠标发出攻击等。

      例如,在小球下落时通过键盘控制其左右移动。由于需要监听键盘事件,所以需要实现KeyListener接口。步骤如下:

      第一步,先实现KeyListener接口,并将为该面板注册键盘事件监听器。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      第二步,设定xpos变量来移动x坐标

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      第三步,重写keyPressed()方法,当按下键盘按键后响应触发

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      完整代码如下:

      class TestPanel extends JPanel implements Runnable, KeyListener {
          int ypos = 0;
          int xpos = 0;
      
          public TestPanel() {
              // 创建一个新线程,this就是实现了Runnable接口的实现类
              Thread t = new Thread(this);
              // 启动线程
              t.start();
              // 设定焦点在本面板并作为监听对象
              setFocusable(true);
              addKeyListener(this);
          }
      
          @Override
          public void paint(Graphics g) {
              g.clearRect(0, 0, this.getWidth(), this.getHeight());// 先清除屏幕上原来的画
              g.setColor(Color.GREEN);// 设置小球的颜色
              g.fillOval(xpos, ypos, 80, 80);// 绘制小球
          }
      
          @Override
          public void run() {
              while (true) {// 线程中的无限循环
                  try {
                      Thread.sleep(30);// 休眠30毫秒
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  ypos += 5;// 修改小球左上角的纵坐标
                  if (ypos > 300) {// 小球离开窗口后重设左上角的纵坐标
                      ypos = 80;
                  }
                  repaint();// 窗口重绘
              }
          }
      
          @Override
          public void keyTyped(KeyEvent e) {
      
          }
      
          @Override
          public void keyPressed(KeyEvent e) {
              // 当键盘的按键按下后触发该事件
              int keyCode = e.getKeyCode();// 获取按键编号
              if (keyCode == KeyEvent.VK_LEFT) {// 当触发Left键时
                  xpos -= 10;
              } else if (keyCode == KeyEvent.VK_RIGHT) {// 当触发Right键时
                  xpos += 10;
              }
              repaint();// 重新绘制窗体图像
          }
      
          @Override
          public void keyReleased(KeyEvent e) {
      
          }
      }
      
      public class Demo {
          public static void main(String[] args) {
              JFrame frame = new JFrame();
              frame.setLocation(200, 200);
              frame.setSize(500, 500);
      
              TestPanel panel = new TestPanel();
              frame.setContentPane(panel);
      
              frame.setVisible(true);
              frame.setResizable(false);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }
      }

      4.6 游戏中的碰撞检测

      所谓的碰撞就是接触,游戏中常见的碰撞算法有矩形碰撞、圆形碰撞和像素碰撞等。

      4.6.1 矩形碰撞

      第1种方法:通过检测一个矩形的4个顶点是否在另一个矩形的内部来完成。

      通常由x和y坐标以及长度和宽度来确定一个矩形,因此又可以利用这四个参数来确定是否发生了碰撞。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      核心的检测处理如下:

          public boolean isCollidingWith(int px, int py) {
              // px和py分别传入的是x坐标和y坐标
              if (px > getX() && px < getX() + getActorWidth() && px > getY() && px < getY() + getActorHeight()) {
                  return true;
              }
              return false;
          }
      
          public boolean isCollidingWith(Actor another) {
              // 判断矩形只要有任何一个点在另一个Actor所表示的矩形范围内,就表示发生了碰撞
              if (isCollidingWith(another.getX(), another.getY()) ||
                      isCollidingWith(another.getX() + another.getActorWidth(), another.getY()) ||
                      isCollidingWith(another.getX(), another.getY() + another.getActorHeight()) ||
                      isCollidingWith(another.getX() + another.getActorWidth(), another.getY() + another.getActorHeight())) {
                  return true;
              }
              return false;
          }

      完整的代码如下:

      public class Actor {
          int x, y, w, h;// 分别是x和y坐标,宽度和高度,构成一个矩形
      
          public Actor() {
          }
      
          public Actor(int x, int y, int w, int h) {
              this.x = x;
              this.y = y;
              this.w = w;
              this.h = h;
          }
      
          public int getX() {
              return x;
          }
      
          public int getY() {
              return y;
          }
      
          public int getActorWidth() {
              return w;
          }
      
          public int getActorHeight() {
              return h;
          }
      
          @Override
          public String toString() {
              return "Actor{" +
                      "x=" + x +
                      ", y=" + y +
                      ", w=" + w +
                      ", h=" + h +
                      '}';
          }
      
          public boolean isCollidingWith(int px, int py) {
              // px和py分别传入的是x坐标和y坐标
              // 等号的情况就是考虑垂直重叠和水平重叠的情况
              // 考虑的情况就是传入的坐标是否在当前的矩形范围内,只要满足下面所有条件就表示传入的坐标在当前矩形范围内,返回true
              if (px >= getX() && px < getX() + getActorWidth() && py >= getY() && py < getY() + getActorHeight()) {
                  return true;
              }
              return false;
          }
      
          // 碰撞检测,发生碰撞返回true,否则返回false
          public boolean isCollidingWith(Actor another) {
              // 判断矩形只要有任何一个点在另一个Actor所表示的矩形范围内,就表示发生了碰撞
              if (isCollidingWith(another.getX(), another.getY()) ||
                      isCollidingWith(another.getX() + another.getActorWidth(), another.getY()) ||
                      isCollidingWith(another.getX(), another.getY() + another.getActorHeight()) ||
                      isCollidingWith(another.getX() + another.getActorWidth(), another.getY() + another.getActorHeight())) {
                  return true;
              }
              return false;
          }
      
          public static void main(String[] args) {
              Actor actor = new Actor(10, 10, 100, 150);
              Actor another = new Actor(20, 50, 100, 150);
              boolean collidingWith = actor.isCollidingWith(another);
              System.out.println(collidingWith);
          }
      }
      

      可以通过如下代码移动矩形来进行动态查看:

      class TestPanel extends JPanel implements KeyListener {
          private int x1 = 20, y1 = 20, x2 = 160, y2 = 20, width = 100, height = 100;
      
          public TestPanel() {
              // 设置焦点并且添加键盘事件监听器
              setFocusable(true);
              addKeyListener(this);
          }
      
          @Override
          public void paint(Graphics g) {
              // 在进行绘制之前,一定要清除之前的图形
              g.clearRect(0, 0, this.getWidth(), this.getHeight());// 先清除屏幕上原来的画
              g.drawRect(x1, y1, width, height);
              g.drawRect(x2, y2, width, height);
          }
      
          @Override
          public void keyTyped(KeyEvent e) {
      
          }
      
          @Override
          public void keyPressed(KeyEvent e) {
              // 处理第一个矩形的移动
              switch (e.getKeyCode()) {
                  case KeyEvent.VK_A:// 'A'键
                      x1 -= 5;
                      break;
                  case KeyEvent.VK_D:// 'D'键
                      x1 += 5;
                      break;
                  case KeyEvent.VK_W:// 'W'键
                      y1 -= 5;
                      break;
                  case KeyEvent.VK_S://'S'键
                      y1 += 5;
                      break;
                  case KeyEvent.VK_LEFT://’LEFT'键
                      x2 -= 5;
                      break;
                  case KeyEvent.VK_RIGHT:// 'RIGHT'键
                      x2 += 5;
                      break;
                  case KeyEvent.VK_UP:// 'UP'键
                      y2 -= 5;
                      break;
                  case KeyEvent.VK_DOWN:// 'DOWN'键
                      y2 += 5;
                      break;
              }
              repaint();// 修改坐标后,重绘图形
              // 判断是否碰撞,输出信息
              Actor actor = new Actor(x1, y1, width, height);
              Actor another = new Actor(x2, y2, width, height);
              System.out.println("是否碰撞:" + (actor.isCollidingWith(another) || another.isCollidingWith(actor)) + "| " + actor + "| " + another);
          }
      
          @Override
          public void keyReleased(KeyEvent e) {
      
          }
      }
      
      public class Demo {
          public static void main(String[] args) {
              JFrame frame = new JFrame();
              frame.setLocation(200, 200);
              frame.setSize(500, 500);
      
              TestPanel panel = new TestPanel();
              frame.setContentPane(panel);
      
              frame.setVisible(true);
              frame.setResizable(false);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }
      }

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      第2种方法:从相反的角度考虑,以前是处理什么时候相交,现在处理什么时候不会相交。如两个矩形a和b来判断4条边,假如a矩形在左边,b矩形在右边,那么可以判断左边a矩形的右边界在b矩形的左边界之外,同理,a的上边界需要在b的下边界以外,4条边都判断,则可以知道a矩形是否与b矩形相交。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      方法如下:

          /**
           * 判断两个矩形是否会发生碰撞
           *
           * @param ax 矩形a的x坐标
           * @param ay 矩形a的y坐标
           * @param aw 矩形a的宽度
           * @param ah 矩形a的高度
           * @param bx 矩形b的x坐标
           * @param by 矩形b的y坐标
           * @param bw 矩形b的宽度
           * @param bh 矩形b的高度
           * @return 如果发生碰撞则返回true,否则返回false
           */
          public boolean isCollidingWith(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) {
              if (ay > by + bh || by > ay + ah || ax > bx + bw || bx > ax + aw) {
                  return false;
              }
              return true;
          }

      第3种方法:是方法2的变异,我们保存两个矩形的左上和右下两个坐标的坐标值,然后对两个坐标的一个对比就可以得出两个矩形是否相交。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

          /**
           * rect1[0]:矩形1左上角x坐标
           * rect1[1]:矩形1左上角y坐标
           * rect1[2]:矩形1右下角x坐标
           * rect1[3]:矩形1右下角y坐标
           * rect2[0]:矩形2左上角x坐标
           * rect2[1]:矩形2左上角y坐标
           * rect2[2]:矩形2右下角x坐标
           * rect2[3]:矩形2右下角y坐标
           *
           * @param rect1 第一个矩形的左上角坐标和右下角坐标数组
           * @param rect2 第二个矩形的左上角坐标和右下角坐标数组
           * @return 如果发生碰撞则返回true,否则返回false
           */
          public static boolean isCollidingWith(int rect1[], int rect2[]) {
              if (rect1[0] > rect2[2]) {
                  return false;
              }
              if (rect1[2] < rect2[0]) {
                  return false;
              }
              if (rect1[1] > rect2[3]) {
                  return false;
              }
              if (rect1[3] < rect2[1]) {
                  return false;
              }
              return true;
          }

      4.6.2 圆形碰撞

      请参考博客:Java游戏中的圆形碰撞检测

      4.6.3 像素碰撞

      由于游戏中的角色的大小往往是以一一个刚好能够将其包围的矩形区域来表示的,如图4-3所示,虽然两个卡通人物并没有发生真正的碰撞,但是矩形碰撞检查的结果却是它们发生了碰撞。

      如果使用像素检查,就通常把精灵的背景颜色设置为相同的颜色而且是最后图片里面很少用到的颜色,然后碰撞检查的时候就只判断两张图片除了背景色外的其他像素区域是否发生了重叠的情况。如图4-4所示,虽然两张图片的矩形发生了碰撞,但是两个卡通人物并没有发生真正的碰撞,这就是像素检查的好处,但缺点是计算复杂,消耗了大量的系统资源,因此一般没有特殊要求,应尽量使用矩形检查碰撞。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      4.7 游戏中图像的绘制

      虽然Graphics类中提供了很多绘制图形的方法,但通常我们一般都是用其他软件如PS画上图像,保存为图片,再利用Java加载图像。

      4.7.1 图像文件的装载

      Java所支持的图像文件格式通常有GIF、PNG和JPEG格式,即带有.gif、.jpg、.jpeg等后缀的文件。

      Java提供了java.awt.Image包来管理与图像文件有关的信息,提供了用于创建、操纵和观察图像的接口和类。

      Toolkit类提供了两个getImage()方法来加载图像:

      Image getImage(URL url);
      Image getImage(String filename);

      Toolkit是一个组件类,获取Toolkit的方法如下:

      Toolkit toolkit = Toolkit.getDefaultTolkit();

      如果类继承了Component组件类,可以直接通过如下方法获得:

      Toolkit toolkit = getToolkit();

      在Java中获取一个图像文件,可以调用Toolkit类提供的getlmageO方法。但是getlmage()方法会在调用后马上返回,如果此时马上使用由getlmage(方法获取的Image 对象,但这时Image对象并没有真正装载或者装载完成。因此,我们在使用图像文件时,使用java.awt 包中的MediaTracker跟踪一个 Image对象的装载,可以保证所有图片都加载完毕。

      使用MediaTracker的步骤如下:

      第一步,实例化MediaTracker,注意要将显示图片的Component对象作为参数传入。

      MediaTracker tracker=new MediaTracker(Jpanel1);

      第二步,将要装载的Image对象加入到MediaTracker中。

      Toolkit toolkit = Toolkit.getDefaultToolkit();
      Image pic = toolkit.getImage("abc.jpg");
      tracker.addImage(pic, 0);

      第三步,调用MediaTracker的checkAll()方法,等待装载过程的结束。

      tracker.checkAll(true);

      4.7.2 图像文件的显示

      getImage()方法只是加载图像文件,如果要显示图像需要Graphics类的drawImage()方法,该方法的重载方法如下:

      public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer);
      public abstract boolean drawImage(Image img, int x, int y, int width, int height,  ImageObserver observer);
      public abstract boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer);
      public abstract boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer);
      public abstract boolean drawImage(Image img,int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer);
      public abstract boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer);

      参数说明:

      • 参数img就是要显示的Image对象。

      • 参数x和y是该图像左上角的坐标值。

      • 参数observer则是一个ImageObserver 接口( intrface ),它用来跟踪图像文件装载是否已经完成的情况,通常我们都将该参数置为this,即传递本对象的引用去实现这个接口。组件可以指定this 作为图像观察者的原因是,Component 类实现了ImageObserver 接口。当图像数据被加载时,它的实现会调用repaint()方法。

      • width和height表示图像显示的高度和宽度。如果实际图像的宽度和高度和设定的这两个参数值不一样则会自动将实际图像进行缩放。

      调用Image类中的两个方法就可以分别得到原图的宽度和高度,它们的调用格式如下,与drawImage( )方法一样,我们通常用this作为observer的参数值(但当前类必须继承一个Component类)。

      int getwidth (ImageObserver observer)
      int getHeight (ImageObserver observer)

      示例如下:

      class TestPanel extends JPanel {
          String filename;
      
          public TestPanel(String filename) {
              this.filename = filename;
          }
      
          @Override
          public void paint(Graphics g) {
              // 获取Image对象,加载图像
              Image img = getToolkit().getImage(filename);
              // 获取图像的宽高
              int w = img.getWidth(this);
              int h = img.getHeight(this);
              // 绘图
              g.drawImage(img, 0, 0, this);
              g.drawImage(img,800,80,w/2,h/2,this);// 缩小一半
          }
      }
      
      public class Demo {
          public static void main(String[] args) {
              JFrame frame = new JFrame();
              frame.setLocation(200, 200);
              frame.setSize(500, 500);
      
              TestPanel panel = new TestPanel("src\\test\\b\\test.jpg");
              frame.setContentPane(panel);
      
              frame.setVisible(true);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }
      }

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      还可以通过javax.imageio.ImageIO类的read()方法获取一幅画像,返回的是BufferedImage对象,BufferedImage是Image的子类,它描述了具有可访问图像数据缓冲区的Image,通过该类即可以实现图片的缩放。

      BufferedImage ImageIO.read(Url);

      绘制该类产生图像的示例代码:

      Image im=ImageIO.read(getClass().getResource("test.jpg"));
      g.drawImage(im,0,0,null);// 图像的绘制方式

      游戏中的场景图像分为两种,分别叫作卷轴型图像( ribbon )和砖块型图像( tile )。卷轴型图像的特点是内容多、面积大,常常用作远景,例如设计游戏中的蓝天白云图像,一般不与用户交互。砖块型图像通常面积很小,往往由多块这样的图像共同组成游戏的前景,并且作为障碍物与游戏角色进行交互,如推箱子游戏中绘制砖块型图像。

      4.7.3 绘制卷轴型图像

      卷轴型图像通常要超过程序窗口的尺寸,事实上,在绘制卷轴型图像时往往都需要让其滚动显示以制造移动效果,让图像的不同部分依次从程序窗口中“经过”,就如同坐火车的情形,卷轴型图像好比风景,程序窗口则好比车窗。用程序实现这样的滚动显示效果,则需要将卷轴型图像一段一 段地 显示在程序窗口中,而这又涉及从图像坐标系到程序窗口坐标系的变换问题。

      如图4-6所示,左边为程序窗口坐标系,原点在窗口左上角;右边为图像坐标系,原点在卷轴型图像区域的左上角。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      图像中的坐标变换可以调用程序窗口的Graphics对象的另一个drawImage()方法实现,该方法有十个参数,如下:

          public abstract boolean drawImage(Image img,
                                            int dx1, int dy1, int dx2, int dy2,
                                            int sx1, int sy1, int sx2, int sy2,
                                            ImageObserver observer);

      参数说明:

      • 其中,第1个参数表示源图像;

      • 第2个至第9个参数的含义如图4-6所示。dxl和dyl为目标区域左上角坐标;dx2和dy2为目标区域右下角坐标;sxl和syl为源区域左上角坐标; sx2和sy2为源区域右下角坐标。

      如果是水平方向的场景滚动,则dy1和dy2、sy1和sy2的值无需改变,可根据窗口的尺寸设置为固定值。

      4.7.4 绘制砖块型图像

      砖块型图像是将窗口区域按照砖块大小进行划分成小方格,然后在对应的小方格内绘制图像。

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      用多个砖块型图像来绘制窗口中的不同区域,需要使用砖块地图( Tile Map)。砖块地图可以简单地使用一个文本文件或者二维数组来保存,记录某个位置显示的图像可以通过图像代号来表示(如推箱子游戏中存储1表示pic1.jpg砖块型图像,存储2表示pic2.jpg砖块型图像,存储0表示不绘制图像等等)。在游戏初始时由程序载人砖块地图文件或者二维数组,并对文件中的信息或者二维数组逐行地进行分析,然后根据不同的图像代号来分别读取不同种类的砖块型图像。

          static byte map[][] = {
                  {0, 0, 1, 1, 1, 0, 0, 0},
                  {0, 0, 1, 4, 1, 0, 0, 0},
                  {0, 0, 1, 9, 1, 1, 1, 1},
                  {1, 1, 1, 2, 9, 2, 4, 1},
                  {1, 4, 9, 2, 5, 1, 1, 1},
                  {1, 1, 1, 1, 2, 1, 0, 0},
                  {0, 0, 0, 1, 4, 1, 0, 0},
                  {0, 0, 0, 1, 1, 1, 0, 0}};

      如走迷宫游戏、连连看游戏、推箱子游戏都采用了砖块型图像技术来实现。

      4.8 游戏角色开发

      游戏角色就是游戏中可以移动的物体,主要包括玩家控制的角色和电脑控制的角色,如怪物等。

      但游戏中需要通过连续绘制多幅静态图像来表示其运动效果。

      我们设计一个Sprite类,主要用来实现游戏里面的人物动画和移动的效果,使用Sprite类读取一张张小图片,并且按照一定的顺序存储在数组中,然后在屏幕上显示出其中的一张小图片,如果连续地更换显示的小图片,则在屏幕上表现为一个完整的动画效果。

      如Sprite类中的人物动画用4帧状态图片构成,表示移动,如下图:

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      public class Sprite {
          // x和y坐标
          public int xPos = 0, yPos = 0;
          // 当前帧的ID,也就是第几张图片
          private int playID = 0;
          // Sprite类的图片数组
          private Image pics[] = null;
          // 是否更新绘制Sprite
          boolean facus = true;
      
          public Sprite() {
              // 加载图片
              pics = new Image[4];
              for (int i = 0; i < 4; i++) {
                  pics[i] = Toolkit.getDefaultToolkit().getImage("src\\test\\a\\images\\Right (" + (i + 1) + ").jpg");
              }
          }
      
          /* 初始化坐标 */
          public void init(int x, int y) {
              this.xPos = x;
              this.yPos = y;
          }
      
          /* 设置坐标 */
          public void set(int x, int y) {
              this.xPos = x;
              this.yPos = y;
          }
      
          /* 绘制角色 */
          public void drawSprite(Graphics g, JPanel panel) {
              g.drawImage(pics[playID], xPos, yPos, (ImageObserver) panel);
              playID++;// 下一帧图像
              if (playID == 4) {// 图像放完后,又从第一帧开始
                  playID = 0;
              }
          }
      
          /* 更新角色的坐标点 */
          public void updateSprite() {
              if (facus) {// 每次移动15个像素
                  xPos += 15;
              }
              if (xPos > 300) {// 如果达到窗口的右边缘
                  xPos = 0;
              }
              System.out.println(xPos);// 打印x坐标
          }
      }
      

      测试类,注意传入的图片路径一定要正确,否则无法加载图像,也不会报错

      class TestPanel extends JPanel implements Runnable {
          private Sprite player;
      
          public TestPanel() {
              // 创建角色
              player = new Sprite();
              player.init(0, 0);
              Thread thread = new Thread(this);// 新建一个线程
              thread.start();// 启动线程
          }
      
          @Override
          public void paint(Graphics g) {
              // 清除屏幕,擦除掉原来画的东西
              g.clearRect(0, 0, getWidth(), getHeight());
              player.drawSprite(g, this);// 绘制角色
          }
      
          @Override
          public void run() {
              // 线程中无限循环
              while (true) {
                  player.updateSprite();// 更新角色的x和y坐标
                  try {
                      Thread.sleep(500);// 休眠500毫秒
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  repaint();// 窗口重绘
              }
          }
      }
      
      public class Demo {
          public static void main(String[] args) {
              JFrame frame = new JFrame();
              frame.setLocation(200, 200);
              frame.setSize(500, 500);
      
              TestPanel panel = new TestPanel();
              frame.setContentPane(panel);
      
              frame.setVisible(true);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          }
      }

      效果如图:

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      在游戏中人物角色通过键盘事件中用户按键来决定方向,从而更新角色的x和y坐标。

      4.9 游戏声音效果的设定

      游戏中的声音效果一般分为两类,分别是动作音效和场景音乐。前者用于动作的配音,以便增强游戏角色的真实感后者用于;烘托游戏气氛,通过为不同的背景配备相对应的音乐来表达特定的情感。
      Java提供了javax.sound.sampled包来对声音进行处理和播放,支持的格式有:AIFF、AU和WAV三种。

      同图像的显示一样,需要先将声音文件装载进来,然后播放,步骤如下:

      第一步,新建一个文件对象获取WAV声音文件数据。

      File file = new File("sound.wav");

      第二步,把WA文件转换为音频输入流

      AudioInputStream stream = AudioSystem.getAudioInputStream(file);

      第三步,获取音频格式

      AudioFormat format = stream.getFormat();

      第四步,设置音频行信息

      DataLine.Info info = new DataLine.Info(Clip.class, format);

      第五步,建立音频行

      Clip clip = (Clip)AudioSystem.getLine(info);

      第六步,将音频数据流读入音频行

      clip.open(stream);

      第七步,播放音频行

      clip.start();

      处理声音数据输人的流类叫作AudiolnputStream, 它是具有指定音频格式和长度的输人流。除此之外,还需要使用AudioSystem类,该类用于充当取样音频系统资源的人口点,提供许多在不同格式间转换音频数据的方法,以及在音频文件和流之间进行转换的方法。

      AudioFormat类是在声音流中指定特定数据安排的类。通过检查以音频格式存储的信息,可以发现在二进制声音数据中解释位的方式。其中涉及的类包括如下几个。

      • Line:Line接口表示单声道或多声道音频供给。
      • DataLine:包括一些音频传输控制方法,这些方法可以启动、停止、消耗和刷新通过数据行传人的音频数据。
      • DataLine.Info:提供音频数据行的信息。包括受数据行支持的音频格式、其内部缓冲区的最小和最大值等。
      • Clip:接口表示特殊种类的数据行,该数据行的音频数据可以在回放前加载,而不是实时流出。音频剪辑的回放可以使用start0和stop0方法开始和终止。这些方法不重新设置介质的位置,start()方法的功能是从回放最后停止的位置继续回放。

      示例如下:

      封装一个声音播放类SoundPlayer.java

      /**
       * 声音播放类
       */
      public class SoundPlayer {
          public File file;
          public AudioInputStream stream;
          public AudioFormat format;
          DataLine.Info info;
          Clip clip;
      
          /**
           * 加载声音文件
           *
           * @param filePath 声音文件的路径
           */
          public void loadSound(String filePath) {
              file = new File(filePath);
              try {
                  stream = AudioSystem.getAudioInputStream(file);
              } catch (UnsupportedAudioFileException | IOException e) {
                  e.printStackTrace();
              }
              format = stream.getFormat();
          }
      
          /**
           * 播放声音
           */
          public void playSound() {
              info = new DataLine.Info(Clip.class, format);
              try {
                  clip = (Clip) AudioSystem.getLine(info);
                  clip.open(stream);
              } catch (LineUnavailableException | IOException e) {
                  e.printStackTrace();
              }
              clip.start();
          }
      
      }

      为角色动作添加动作音乐,在用户按键盘的空格键时播放音效,代码如下:

      《Java游戏编程原理与实践教程》读书笔记(第4章——Java游戏程序的基本框架)

      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.csdn.net/cnds123321/article/details/117751459,作者:二木成林,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:从零开始手撸WebGL3D引擎11: PostProcessing框架(里程碑5了)

      下一篇:Python的PyQt框架的使用-布局管理篇

      相关文章

      2025-05-14 10:33:16

      30天拿下Rust之字符串

      在Rust中,字符串是一种非常重要的数据类型,用于处理文本数据。Rust的字符串是以UTF-8编码的字节序列,主要有两种类型:&str和String。其中,&str是一个对字符数据的不可变引用,更像是对现有字符串数据的“视图”,而String则是一个独立、可变更的字符串实体。

      2025-05-14 10:33:16
      amp , Rust , str , String , 使用 , 字符串 , 方法
      2025-05-14 10:33:16

      30天拿下Rust之向量

      在Rust语言中,向量(Vector)是一种动态数组类型,可以存储相同类型的元素,并且可以在运行时改变大小。向量是Rust标准库中的一部分,位于std::vec模块中。

      2025-05-14 10:33:16
      Rust , 使用 , 元素 , 向量 , 方法 , 索引 , 迭代
      2025-05-14 10:03:05

      30天拿下Rust之HashMap

      HashMap,被称为哈希表或散列表,是一种可以存储键值对的数据结构。它使用哈希函数将键映射到存储位置,以便可以快速检索和更新元素。

      2025-05-14 10:03:05
      HashMap , 使用 , 哈希 , 引用 , 方法 , 遍历 , 键值
      2025-05-14 10:02:58

      java休眠到指定时间怎么写

      java休眠到指定时间怎么写

      2025-05-14 10:02:58
      java , sleep , Thread , util , 方法
      2025-05-14 10:02:58

      30天拿下Rust之结构体

      在Rust语言中,结构体是一种用户自定义的数据类型,它允许你将多个相关的值组合成一个单一的类型。结构体是一种复合数据类型,可以用来封装多个不同类型的字段,这些字段可以是基本数据类型、其他结构体、枚举类型等。通过使用结构体,你可以创建更复杂的数据结构,并定义它们的行为。

      2025-05-14 10:02:58
      Rust , 使用 , 字段 , 实例 , 方法 , 示例 , 结构
      2025-05-14 10:02:48

      typescript 将数组清空

      在TypeScript或JavaScript开发中,数组是用于存储和管理一组数据的基础数据结构。当需要清空一个数组时,有多种方法可以实现,而选择合适的方法不仅影响代码的可读性,还会对性能产生一定的影响。不同场景下,选择适合的清空数组的方法至关重要。

      2025-05-14 10:02:48
      length , pop , 引用 , 数组 , 方法
      2025-05-14 09:51:21

      java 判断map为null或者空

      java 判断map为null或者空

      2025-05-14 09:51:21
      Java , Map , null , 方法 , 是否 , 检查 , 示例
      2025-05-14 09:51:15

      JAVA 两个类同时实现同一个接口

      在Java中,两个类同时实现同一个接口是非常常见的。接口定义了一组方法,实现接口的类必须提供这些方法的具体实现。

      2025-05-14 09:51:15
      Lambda , 函数 , 实现 , 接口 , 方法 , 表达式
      2025-05-14 09:51:15

      java怎么对线程池做监控

      对Java线程池进行监控是确保系统性能和稳定性的重要部分。监控线程池可以帮助我们了解线程池的状态,如当前活跃线程数、任务队列长度、已完成任务数等。

      2025-05-14 09:51:15
      Java , 方法 , 监控 , 示例 , 线程 , 队列
      2025-05-13 09:53:23

      java动态获取实体类的字段

      在Java中,我们可以使用反射(Reflection)API来动态地获取实体类的字段。

      2025-05-13 09:53:23
      API , Java , 使用 , 字段 , 实体类 , 方法 , 获取
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5248853

      查看更多

      最新文章

      深入理解 ThreadLocal 的线程安全机制与实践应用

      2025-03-31 08:58:01

      【java虚拟机】JVM知识框架快速一览

      2025-03-11 09:34:18

      深入浅出揭秘ModernUI Framework

      2025-03-10 09:52:02

      Python 中如何实现参数化测试?

      2025-02-25 08:55:28

      C++游戏开发详解:从入门到实践

      2024-10-29 09:42:02

      查看更多

      热门文章

      C++游戏开发详解:从入门到实践

      2024-10-29 09:42:02

      深入浅出揭秘ModernUI Framework

      2025-03-10 09:52:02

      Python 中如何实现参数化测试?

      2025-02-25 08:55:28

      【java虚拟机】JVM知识框架快速一览

      2025-03-11 09:34:18

      深入理解 ThreadLocal 的线程安全机制与实践应用

      2025-03-31 08:58:01

      查看更多

      热门标签

      客户端 实践 基础知识 Java 服务器 java 数据库 框架 python 服务端 学习 代码 简单 javascript 编程
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      深入浅出揭秘ModernUI Framework

      Python 中如何实现参数化测试?

      【java虚拟机】JVM知识框架快速一览

      深入理解 ThreadLocal 的线程安全机制与实践应用

      C++游戏开发详解:从入门到实践

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号