java衍生作用_java-如何从AffineTransform衍生的形状对象中“...

您可以使用AffineTransform.transform(Point2D, Point2D)变换多边形上的单个点.

如果您不使用旋转变换来移动船,而是将船的位置保持在一个(x,y)位置,那么事情就简单得多.您可以在move()中移动飞船的位置,而不是尝试平移多边形.然后,当您想给船上油漆时,例如做:

// Optionally copying the Graphics so the

// transform doesn't affect later painting.

Graphics2D temp = (Graphics2D) g2d.create();

temp.translate(ship.locX, ship.locY);

temp.rotate(ship.angle);

temp.draw(ship);

要基于速度移动点,可以执行以下操作来找到运动向量:

double velX = speed * Math.cos(angle);

double velY = speed * Math.sin(angle);

locX += timeElapsed * velX;

locY += timeElapsed * velY;

这实质上是从极坐标到笛卡尔坐标的转换. x和y速度是三角形的边,其斜边为速度,已知角度为角度:

/|

/ |

/ |

/ |

speed / |

/ |

/ |velY

/ angle |

/)_______|

velX

对于您的评论:

Are you saying that, unlike my initial move function, just to make ship hold a single point, and thus I would only translate that instead?

或多或少,是的.您仍然需要有一个多边形来保持船的形状,但是多边形上的点将相对于(0,0).

假设以下定义:

>多边形上的每个(x,y)点都是pi. (换句话说,p0,p1,p2和p3中的一个.)

>平移的(x,y)坐标为T

然后,在转换Graphics2D之后,每个pi坐标在面板上变为piT.因此,如果您的多边形点是相对于(0,0)定义的,则平移到飞船的(locX,locY)会将多边形移动到相对于(locX,locY)的位置.

然后,将多边形的尖端定义为(0,0)可能是最简单的,这样在平移之后,船的尖端就是船的位置:

// Your original points:

int xPoints[] = {800, 780, 800, 820};

int yPoints[] = {400, 460, 440, 460};

// Become these points relative to (0,0):

int xPoints[] = {0, -20, 0, 20};

int yPoints[] = {0, 60, 40, 60};

和例如在同一个地方开船,您将其位置初始化为(800,400).

我又想了一下,意识到旋转有点复杂,因为您可能不想绕着尖端旋转船.您可能想绕船的中心旋转船.

因此,这里有一个MCVE演示了如何进行所有这些操作.

NCtPx.png

package mcve.game;

import javax.swing.*;

import java.awt.event.*;

import java.awt.geom.*;

import java.awt.Polygon;

import java.awt.RenderingHints;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Color;

import java.awt.Dimension;

import java.awt.Rectangle;

import java.awt.Insets;

import java.awt.Toolkit;

import java.awt.GraphicsConfiguration;

import java.util.Set;

import java.util.HashSet;

import java.util.List;

import java.util.ArrayList;

public class MovementExample implements ActionListener {

public static void main(String[] args) {

SwingUtilities.invokeLater(MovementExample::new);

}

final int fps = 60;

final int period = 1000 / fps;

final JFrame frame;

final GamePanel panel;

final Controls controls;

final Ship ship;

final List bullets = new ArrayList<>();

MovementExample() {

frame = new JFrame("Movement Example");

Dimension size = getMaximumWindowSize(frame);

size.width /= 2;

size.height /= 2;

frame.setPreferredSize(size);

panel = new GamePanel();

frame.setContentPane(panel);

frame.pack();

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setLocationRelativeTo(null);

frame.setVisible(true);

controls = new Controls();

ship = new Ship(panel.getWidth() / 2,

panel.getHeight() / 2);

new Timer(period, this).start();

}

@Override

public void actionPerformed(ActionEvent e) {

double secondsElapsed = 1.0 / fps;

ship.update(secondsElapsed);

bullets.forEach(b -> b.update(secondsElapsed));

Rectangle bounds = panel.getBounds();

bullets.removeIf(b -> !bounds.contains(b.locX, b.locY));

panel.repaint();

}

class GamePanel extends JPanel {

GamePanel() {

setBackground(Color.WHITE);

}

@Override

protected void paintComponent(Graphics g) {

super.paintComponent(g);

Graphics2D g2 = (Graphics2D) g.create();

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

if (ship != null) {

ship.draw(g2);

}

bullets.forEach(b -> b.draw(g2));

g2.dispose();

}

}

abstract class AbstractGameObject {

double maxSpeed;

double rotationAngle;

double locX;

double locY;

double velX;

double velY;

AbstractGameObject(double initialX, double initialY) {

locX = initialX;

locY = initialY;

}

abstract void update(double secondsElapsed);

abstract void draw(Graphics2D g2);

}

class Ship extends AbstractGameObject {

Polygon shape;

double rotationRate;

Ship(double initialX, double initialY) {

super(initialX, initialY);

maxSpeed = 128; // pixels/second

rotationAngle = Math.PI * 3 / 2;

rotationRate = (2 * Math.PI) / 2; // radians/second

int xPoints[] = {0, -20, 0, 20};

int yPoints[] = {0, 60, 40, 60};

shape = new Polygon(xPoints, yPoints, 4);

}

Point2D.Double getTip() {

Point2D.Double center = getCenter();

// The tip is at (0,0) and it's already centered

// on the x-axis origin, so the distance from the

// tip to the center is just center.y.

double distance = center.y;

// Then find the location of the tip, relative

// to the center.

double tipX = distance * Math.cos(rotationAngle);

double tipY = distance * Math.sin(rotationAngle);

// Now find the actual location of the center.

center.x += locX;

center.y += locY;

// And return the actual location of the tip, relative

// to the actual location of the center.

return new Point2D.Double(tipX + center.x, tipY + center.y);

}

Point2D.Double getCenter() {

// Returns the center point of the ship,

// relative to (0,0).

Point2D.Double center = new Point2D.Double();

for (int i = 0; i < shape.npoints; ++i) {

center.x += shape.xpoints[i];

center.y += shape.ypoints[i];

}

center.x /= shape.npoints;

center.y /= shape.npoints;

return center;

}

@Override

void update(double secondsElapsed) {

// See my answer here: https://stackoverflow.com/a/43692434/2891664

// for a discussion of why this logic is the way it is.

double speed = 0;

if (controls.isUpHeld()) {

speed += maxSpeed;

}

if (controls.isDownHeld()) {

speed -= maxSpeed;

}

velX = speed * Math.cos(rotationAngle);

velY = speed * Math.sin(rotationAngle);

locX += secondsElapsed * velX;

locY += secondsElapsed * velY;

double rotation = 0;

if (controls.isLeftHeld()) {

rotation -= rotationRate;

}

if (controls.isRightHeld()) {

rotation += rotationRate;

}

rotationAngle += secondsElapsed * rotation;

// Cap the angle so it can never e.g. get so

// large that it loses precision.

if (rotationAngle > 2 * Math.PI) {

rotationAngle -= 2 * Math.PI;

}

if (controls.isFireHeld()) {

Point2D.Double tipLoc = getTip();

Bullet bullet = new Bullet(tipLoc.x, tipLoc.y, rotationAngle);

bullets.add(bullet);

}

}

@Override

void draw(Graphics2D g2) {

Graphics2D copy = (Graphics2D) g2.create();

copy.setColor(Color.RED);

// Translate to the ship's location.

copy.translate(locX, locY);

// Rotate the ship around its center.

Point2D.Double center = getCenter();

// The PI/2 offset is necessary because the

// polygon points are defined with the ship

// already vertical, i.e. at an angle of -PI/2.

copy.rotate(rotationAngle + (Math.PI / 2), center.x, center.y);

copy.fill(shape);

}

}

class Bullet extends AbstractGameObject {

Ellipse2D.Double shape = new Ellipse2D.Double();

Bullet(double initialX, double initialY, double initialRotation) {

super(initialX, initialY);

maxSpeed = 512;

rotationAngle = initialRotation;

velX = maxSpeed * Math.cos(rotationAngle);

velY = maxSpeed * Math.sin(rotationAngle);

double radius = 3;

shape.setFrame(-radius, -radius, 2 * radius, 2 * radius);

}

@Override

void update(double secondsElapsed) {

locX += secondsElapsed * velX;

locY += secondsElapsed * velY;

}

@Override

void draw(Graphics2D g2) {

Graphics2D copy = (Graphics2D) g2.create();

copy.setColor(Color.BLACK);

copy.translate(locX, locY);

copy.fill(shape);

}

}

// See https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html

class Controls {

final Set keysHeld = new HashSet<>();

Controls() {

bind(KeyEvent.VK_A, "left");

bind(KeyEvent.VK_D, "right");

bind(KeyEvent.VK_W, "up");

bind(KeyEvent.VK_S, "down");

bind(KeyEvent.VK_SPACE, "fire");

}

boolean isLeftHeld() { return keysHeld.contains(KeyEvent.VK_A); }

boolean isRightHeld() { return keysHeld.contains(KeyEvent.VK_D); }

boolean isUpHeld() { return keysHeld.contains(KeyEvent.VK_W); }

boolean isDownHeld() { return keysHeld.contains(KeyEvent.VK_S); }

boolean isFireHeld() { return keysHeld.contains(KeyEvent.VK_SPACE); }

void bind(int keyCode, String name) {

bind(keyCode, name, true);

bind(keyCode, name, false);

}

void bind(int keyCode, String name, boolean isOnRelease) {

KeyStroke stroke = KeyStroke.getKeyStroke(keyCode, 0, isOnRelease);

name += isOnRelease ? ".released" : ".pressed";

panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)

.put(stroke, name);

panel.getActionMap()

.put(name, new AbstractAction() {

@Override

public void actionPerformed(ActionEvent e) {

if (isOnRelease) {

keysHeld.remove(keyCode);

} else {

keysHeld.add(keyCode);

}

}

});

}

}

// This returns the usable size of the display which

// the JFrame resides in, as described here:

// http://docs.oracle.com/javase/8/docs/api/java/awt/GraphicsEnvironment.html#getMaximumWindowBounds--

static Dimension getMaximumWindowSize(JFrame frame) {

GraphicsConfiguration config = frame.getGraphicsConfiguration();

Dimension size = config.getBounds().getSize();

Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);

size.width -= insets.left + insets.right;

size.height -= insets.top + insets.bottom;

return size;

}

}

还有其他方法可以计算出船的顶端,但是我在MCVE中做到的方式是:

>获取相对于(0,0)的船的中心点.

>获取从中心点到尖端的距离.尖端在(0,0),因此这只是中心的y坐标.

>然后计算尖端相对于中心的(x,y)位置.速度和速度与上图非常相似,但是斜边是船的中心与船头之间的距离.

>将中心平移为相对于船的位置.

>将尖端的位置(相对于中心)转换为相对于船的位置.

也可以使用AffineTransform完成所有操作,类似于您在问题代码中所做的操作,但是您需要在每次更新时进行设置.像这样:

AffineTransform transform = new AffineTransform();

@Override

void update(double secondsElapsed) {

...

// Clear the previous translation and rotation.

transform.setToIdentity();

// Set to current.

transform.translate(locX, locY);

Point2D.Double center = getCenter();

transform.rotate(rotationAngle + (Math.PI / 2), center.x, center.y);

if (controls.isFireHeld()) {

Point2D.Double tip = new Point2D.Double(0, 0);

transform.transform(tip, tip);

Bullet bullet = new Bullet(tip.x, tip.y, rotationAngle);

bullets.add(bullet);

}

}

您仍然可以通过这种方式使用变换来进行计算,但最终不会因依赖于移动而产生任何怪异. (在问题代码中,例如,船舶仅沿y轴移动.明显的侧向移动是由于一系列旋转串联所致.)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/393306.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程间通信 (IPC) 方法总结(三)

进程间通信 &#xff08;IPC&#xff09; 方法总结&#xff08;三&#xff09; 信号量&#xff08;SEMAPHORE&#xff09; 信号量是一个计数器&#xff0c;用于多进程对共享数据的访问&#xff0c;信号量的意图在于进程间同步。 为了获得共享资源&#xff0c;进程需要执行下列操…

实现离线加域---Windows2008 R2 新功能系列之八

我们都知道&#xff0c;部署活动目录&#xff0c;无非搭建一台或多台DC&#xff0c;然后把其它的客户端计算机或成员服务器全部加入域&#xff0c;但在windows2008SP2以前&#xff0c;客户端加入域时&#xff0c;DC必须在线&#xff0c;而从2008R2开始我们已经可以做到让客户端…

分表后需要注意的二三事

前言 本篇是上一篇《一次分表踩坑实践的探讨》&#xff0c;所以还没看过的朋友建议先看上文。 还是先来简单回顾下上次提到了哪些内容&#xff1a; 分表策略&#xff1a;哈希、时间归档等。分表字段的选择。数据迁移方案。而本篇文章的背景是在我们上线这段时间遇到的一些问题并…

DNS 原理

阮老师的作品&#xff0c;非常精彩&#xff0c;转载&#xff01; DNS 是互联网核心协议之一。不管是上网浏览&#xff0c;还是编程开发&#xff0c;都需要了解一点它的知识。 本文详细介绍DNS的原理&#xff0c;以及如何运用工具软件观察它的运作。我的目标是&#xff0c;读完此…

销售员/学员/讲师系统

前言: 今晚写一篇关于学员/讲师/销售员CRM系统。这个小项目是27号开始做的&#xff0c;大概搞了一星期不到。我把一些知识点总结下&#xff0c;还写下当时克服的BUG。 Django练习小项目&#xff1a;学员管理系统设计开发 带着项目需求学习是最有趣和效率最高的&#xff0c;今天…

java里面的 |运算符_Java 中 | ^ 运算符的简单使用

背景今天碰到了代码中的按位与运算&#xff0c;复习一下&#xff0c;先列一个各个进制数据表。顺便复习一下十进制转二进制的计算方式&#xff1a;接下来解释下这三个运算符&#xff1a;&  按位与&#xff0c;都转为二进制的情况下&#xff0c;同为1则为1&#xff0c;否则…

彻底理解正向代理、反向代理、透明代理

套用古龙武侠小说套路来说&#xff0c;代理服务技术是一门很古老的技术&#xff0c;是在互联网早期出现就使用的技术。一般实现代理技术的方式就是在服务器上安装代理服务软件&#xff0c;让其成为一个代理服务器&#xff0c;从而实现代理技术。常用的代理技术分为正向代理、反…

使用showMessageDialog显示消息框

-----------------siwuxie095 工程名&#xff1a;TestJOptionPane 包名&#xff1a;com.siwuxie095.showdialog 类名&#xff1a;TestMessageDialog.java 工程结构目录如下&#xff1a; 代码&#xff1a; package com.siwuxie095.showdialog; import java.awt.BorderLayout;…

NodeJS学习笔记(一)——搭建开发框架Express,实现Web网站登录验证

目录 开发环境  1、建立工程  2、目录结构  3、Express配置文件  4、Ejs模板  5、安装常用库及页面分离  6、路由  7、session  8、页面访问控制及提示JS是脚本语言&#xff0c;脚本语言都需要一个解析器才能运行。对于写在HTML页面里 的JS&#xff0c;浏览器充…

项目经理如何管理情绪?这三本书管理书籍你必须要看

本文主要是介绍三本管理的书籍&#xff0c;需要全部书籍的可以加Q群375508415去拿走。里面很多大神的PMP资料。 大家有没有觉得项目经理有时像个政委&#xff0c;做员工思想工作&#xff1b; 有时像个HR&#xff0c;操心员工的稳定和发展&#xff1b; 有时像个咨询顾问&#xf…

[RN] React Native 自定义导航栏随滚动渐变

React Native 自定义导航栏随滚动渐变 实现效果预览&#xff1a; 代码实现&#xff1a; 1、定义导航栏 NavPage.js import React, {Component} from react; import {View, Text, Image, StyleSheet, TouchableOpacity, Platform, Dimensions} from react-native;/*** 自定义导航…

【CSS 技能提升】 :before和:after的使用

前几天的晚上较全面的去看了下css的一些文档和资料&#xff0c;大部分的样式运用都没什么大问题了&#xff0c;只是有些许较陌生&#xff0c;但是也知道他们的存在和实现的是什么样式。今天主要想在这篇学习笔记中写的也不多&#xff0c;主要是针对:before和:after写一些内容&a…

成功试验基于C#/.NET的Android开发

今天最开心事情莫过于摸索验证了一个事情&#xff0c;C#也能进行Android和IOS开发&#xff0c;白天安装了开发环境&#xff0c;晚上进行测试&#xff0c;直到此时此刻&#xff0c;已经成功的导出一款基于C#/.NET的安卓APK&#xff0c;并且能够成功的导入到安卓手机运行&#xf…

深入理解了MySQL,你才能说熟悉数据库

先抛出几个问题 1.为什么不建议使用订单号作为主键?2.为什么要在需要排序的字段上加索引?3.for update 的记录不存在会导致锁住全表?4.redolog 和 binlog 有什么区别?5.MySQL 如何回滚一条 sql ?6.char(50) 和 varchar(50) 效果是一样的么?索引知识回顾 对于 MySQL 数据库…

网站QQ全屏PHP代码,QQ技术导航升级版 超级导航美化版带后台版 PHP源码

QQ技术导航升级版 超级导航美化版带后台版改进F2样式&#xff0c;主针对QQ教程网、卡盟、博客、提供更好收录的位置。改进QQ技术导航背景&#xff0c;增加整体美观效果。去掉死链页面&#xff0c;站长操作使用更加有扩大空间。优化后台登陆界面&#xff0c;去掉织梦后台携带的广…

MySQL基础操作(一)

MySQL操作 一、创建数据库 # utf-8 CREATE DATABASE 数据库名称 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;# gbk CREATE DATABASE 数据库名称 DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci; 二、用户管理 创建用户create user 用户名IP地址 identified by 密码; 删…

集合框架05

一、HashSet集合 1 public class Demo01 {2 /*3 * Set接口&#xff0c;特点不重复元素&#xff0c;没索引4 * Set接口的实现类&#xff0c;HashSet(哈希表)5 * 特点&#xff1a;无序集合&#xff0c;存储和取出的顺序不同&#xff0c;没有索引&#xff0c;不…

HIVE-分桶表的详解和创建实例

我们学习一下分桶表&#xff0c;其实分区和分桶这两个概念对于初学者来说是比较难理解的。但对于理解了的人来说&#xff0c;发现又是如此简单。 我们先建立一个分桶表&#xff0c;并尝试直接上传一个数据 create table student4(sno int,sname string,sex string,sage int, sd…

51nod1270(dp)

题目链接&#xff1a;http://www.51nod.com/onlineJudge/questionCode.html#!problemId1270 题意&#xff1a;中文题诶&#xff5e; 思路&#xff1a;dp sabs(a1-a0)abs(a2-a1).... 要使s尽量大&#xff0c;需要让abs(ai-ai-1)尽量大&#xff0c;那么可以让其中一个尽量小&…

Windows IIS 日志分析研究(Log Parser Log Parser Lizard Log Parser Studio) update...

Windows主要有以下三类日志记录系统事件&#xff1a;应用程序日志、系统日志和安全日志。 存放目录&#xff1a;X:\Windows\System32\winevt\Logs\ System.evtx 系统日志 Application.evtx 应用程序日志 Security.evtx 安全日志 审核策略与事件查看器 # 管理工具 → 本地安全…