還記得之前做的滿天星的項目嗎?還記得怎麼實現嗎?忘記了回去看看,再把代碼寫一遍,在講解滿天星項目的時候,我在最後提到過要怎麼讓這些星星落下來,不知大家去實現過了沒?不過這些都不是重點,今天我將帶大家一起來做一遍。
好的,直接開講,還是老樣子,我們先把項目分解,要分成那幾步去做
1.實現滿天星
2.讓星星落下來
隨著你的編程能力越來越強,每一步能夠駕馭的代碼會越來越多。
下面的代碼實現了滿天星,又加上了線程,在線程裡加入了不斷循環,延時和重畫,這些都是我們已經學習過的內容,下面我要放上源代碼了,最好是自己去實現,若是不行,再回頭看看還有哪一個點是不清楚的
import java.awt.*;
public class MySnow {
public static void main(String[] args) {
// TODO Auto-generated method stub
Frame w = new Frame();
w.setSize(1024,768);
w.setBackground(Color.BLACK);
MySnowPanel msp = new MySnowPanel();
w.add(msp);
Thread t = new Thread(msp);
t.start();
w.show();
}
}
class MySnowPanel extends Panel implements Runnable{
public void paint(Graphics g){
g.setColor(Color.WHITE);
for(int i=0;i<300;i++){
g.drawString("*", (int)(Math.random()*1024),(int)(Math.random()*768));
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
}
如果你的代碼和我的相似,那麼最後的結果是不是和我一樣最後是雪花滿屏幕的亂飛,是風太大嗎?當然還是要思考一樣為什麼會這個樣子,道理還是很簡單,看看最後的repaint()的方法,每次重畫全新的300顆星星的位置全部都會重置。
如果喜歡每顆星星都像小球一樣,緩慢的落下來,我們就不能再paint方法裡生成隨機數來確定每一個坐標,而是需要提前准備好300個隨機的坐標,並且將這些隨機數存下來,每次去修改300個y坐標。然而現在我們有兩個問題要解決:一是我們需要300個X坐標和Y坐標,也就是說我們要聲明600個變量來存放預先生成的隨機數;二是我們需要在paint方法運行前產生所有的隨機數。產生大量相同類型的變量,用數組,事實上數組還有一個原則——通常將含義相同的變量定義為一個數組。數組其實也就是批量聲明變量的工具
class MySnowPanel extends Panel implements Runnable{
int x[]=new int[300];
int y[] = new int [300];
public void paint(Graphics g){
g.setColor(Color.WHITE);
for(int i=0;i<300;i++){
g.drawString("*", (int)(Math.random()*1024),(int)(Math.random()*768));
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
}
仔細看一下數組的定義,很像生成對象的代碼,一個重要重要的標志就是new,這說明是在堆裡申請一個空間,不同之處在於[],這是數組特有的標志。
有了這600個變量,我們需要解決兩個問題,即將隨機數一次性裝到這些變量中。
class MySnowPanel extends Panel implements Runnable{
int x[ ]=new int[300];
int y[ ] = new int [300] ;
for(int i=0;i<300;i++){
x[i]=(int)(Math.random()*1024);
y[i]=(int)(Math.random()*768);
}
public void paint(Graphics g){
g.setColor(Color.WHITE);
for(int i=0;i<300;i++){
g.drawString("*", (int)(Math.random()*1024),(int)(Math.random()*768));
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
}
如果按照這樣子修改,把新的代碼加載paint的方法之上,看看行不行,好像這段代碼有問題,在我的eclipse中還沒有編譯就直接報錯了,是不是和我一樣
那我們來分析一下為啥子不可以,我把新的代碼放到了數組聲明的下面,在paint方法的上面,這也就是告訴編譯器,變量聲明和方法聲明處在了同一級,MySnowPanel類的下一層,這是不被允許的,也就難怪eclipse會報錯了,類裡面是不可以有語句的,在Java程序中,第一層是類,類裡面就是第二層,允許有變量和方法,第三層在方法裡面,允許有變量和語句,不過例外總是存在,會在後面提到,不過現在就不考慮了,免得混淆。
[圖片]
看來我們需要找一個方法,在這個方法裡面賦值,要求這個方法的運行在paint()被調用前。我們知道paint()方法第一次被調用的是窗體顯示的時候,我們用w.add(mp)將MySnowPanel放到窗體上,我們大體可以認為這個操作之後,paint方法就已經被調用了,所以我們需要在w.add(mp)這個操作前賦值,但是不能將賦值操作放到main裡,因為main裡x和y不可見,還必須放到MySnowPanel裡。
有了這樣的分析,我們完全就可以這樣做,把賦值語句放到aaa裡,在main裡調用aaa方法<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
class MySnowPanel extends Panel implements Runnable{
int x[ ]=new int[300];
int y[ ] = new int [300] ;
public void aaa(){
for(int i=0;i<300;i++){
x[i]=(int)(Math.random()*1024);
y[i]=(int)(Math.random()*768);
}
}
public void paint(Graphics g){
g.setColor(Color.WHITE);
for(int i=0;i<300;i++){
g.drawString("*", x[i],y[i]);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
現在eclipse就不會報錯了吧,不過這代碼我看著不舒服,不知道各位看了什麼感覺,Java考慮到這種對象一出現就需要的操作還很多,於是設計了一個機制,叫做構造方法。首先明確的是設計者定義了一個方法,一個特別的方法,舉Frame w = new Frame()這句話,我們看到這裡有個小括弧,好像每一個生成對象的語句都有一個小括弧在這個位置上,包括小括弧很像是方法的調用,沒錯,這個地方確實調用了一個方法,這個方法的名字就是類名,是不是很特別,也就是說,假如你聲明了一個方法,名字就是類名,那麼這個方法就會在生成對象的時候被自動調用。還有個問題,聲明這個方法的時候,返回值應該是什麼?按照調用來,應該是w類型,就是這個類,這是你改變不了得,索性定義這機制的人就規定構造方法聲明的時候不能用返回值。
class MySnowPanel extends Panel implements Runnable{
int x[ ]=new int[300];
int y[ ] = new int [300] ;
//構造方法
public MySnowPanel(){
for(int i=0;i<300;i++){
x[i]=(int)(Math.random()*1024);
y[i]=(int)(Math.random()*768);
}
}
public void paint(Graphics g){
g.setColor(Color.WHITE);
for(int i=0;i<300;i++){
g.drawString("*", x[i],y[i]);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
定義下來,構造方法和類同名,沒有返回值的方法,它會在類生成對象的同時被自動調用,肯定有人會問那麼構造方法到底是干嘛的,一個廣泛的流傳的解釋是,構造方法是用來初始化的。沒錯,大多數情況下構造方法是用來初始化的,就像這個案例,我們用構造方法給數組填上初值,但是這個解釋我並不認同,它是干嘛的,要看你在裡面寫了什麼代碼,構造方法只是一個機制,讓你在對象生成的那一刻瞬間有機會做一些你想要做的事情而已。
好了!我們有300個隨機的X和300個堆積的y,下一步要在線程循環裡讓每個y的值加一。別忘了,落到屏幕下面的雪花,需要讓它回到屏幕上方,這樣才能產生連續不斷的下雪場面。
下面我把最後的代碼放上來
import java.awt.*;
public class MySnow {
public static void main(String[] args) {
// TODO Auto-generated method stub
Frame w = new Frame();
w.setSize(1024,768);
w.setBackground(Color.BLACK);
MySnowPanel msp = new MySnowPanel();
w.add(msp);
Thread t = new Thread(msp);
t.start();
w.show();
}
}
class MySnowPanel extends Panel implements Runnable{
int x[ ]=new int[300];
int y[ ] = new int [300] ;
//構造方法
public MySnowPanel(){
for(int i=0;i<300;i++){
x[i]=(int)(Math.random()*1024);
y[i]=(int)(Math.random()*768);
}
}
public void paint(Graphics g){
g.setColor(Color.WHITE);
for(int i=0;i<300;i++){
g.drawString("*", x[i],y[i]);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
for(int i=0;i<300;i++){
y[i]++;
if(y[i]>768){
y[i]=0;
}
}
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
}
看看結果
到這裡了,你可以寫出很多東西了,比如小球的碰撞,雪花隨風飄揚,利用三角函數可以讓小球做拋物運動等等,還可以用鍵盤控制小球,或者說天上下的不是雪花而是字母,在落地前你按著鍵盤字母會消失,但是這已經是一個打字母的游戲了,不過這個是下一期的更新內容,還是請大家多多練習,多多理解。今天就到這裡,下期再見!