mein erstes 2D spiel

Übersicht BlitzBasic Beginners-Corner

Gehe zu Seite Zurück  1, 2

Neue Antwort erstellen

LukasBanana

BeitragSo, Apr 08, 2007 19:11
Antworten mit Zitat
Benutzer-Profile anzeigen
@bone: Ich kann dir jetzt leider auch schlecht BeispielCodes in das Forum stellen, vielleicht aber doch ein kleines wenn ich mal extra ein Beispiel zum lernen gemacht habe.
Versuch einfach mit den Beispielen, die dir bei der Installation von B3D mitgeliefert wurden, etwas herum zu üben, zu testen was passiert wenn man Werte und Parameter verändert! Ich habe mir Blitz3D NUR durch Beispiele beigebracht! Es sind ja einige vorhanden sofern du diese Beispiele hast.
Aber wie gesagt ich guck mal was ich die Tage für kleine LernBeispiele für dich schreiben kann, Wink

bis dann Gruß LukasBanana
Mein YouTube Channel: http://www.youtube.com/user/SoftPixel
Mein Twitter Profil: https://twitter.com/LukasBanana
Meine Projekte auf github.com: https://github.com/LukasBanana
Meine Projekte auf bitbucket.org: https://bitbucket.org/LukasBanana

bone

BeitragSo, Apr 08, 2007 21:17
Antworten mit Zitat
Benutzer-Profile anzeigen
hey danke finde ich foll net von dir und die idee mit den cods rum zu probieren is eine gute idee danke


MfG: bone

LukasBanana

BeitragSo, Apr 08, 2007 21:23
Antworten mit Zitat
Benutzer-Profile anzeigen
Keine Uhrsache Mr. Green ! Ich werde mich auch gleich mal an ein Beispiel für Blitz3D begeben, dauert halt noch etwas

Gruß LukasBanana
Mein YouTube Channel: http://www.youtube.com/user/SoftPixel
Mein Twitter Profil: https://twitter.com/LukasBanana
Meine Projekte auf github.com: https://github.com/LukasBanana
Meine Projekte auf bitbucket.org: https://bitbucket.org/LukasBanana

Axe

BeitragMo, Apr 09, 2007 10:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Also ich habe mit einer Version von Pong angefangen. Das ist für den Anfang nicht so schwer und man hat schnell was fertig. Später kann man auch hier noch etliche Sachen hinzu fügen: zB einige Extras, die mit der Kugel eingesammelt werden können oder auch einen Netzwerkmodus.

Zu Ping Pong 2D habe ich in den Wikibooks ein Tut geschrieben, das kannst du als Vorlage für eigene Weiterentwicklungen nutzen. Zu dem Buch

MfG Axe
Projekte:
Ping Pong 2D Version 2.1
Weitere Infos: axesite.de

LukasBanana

BeitragDi, Apr 10, 2007 20:11
Antworten mit Zitat
Benutzer-Profile anzeigen
So, ich habe jetzt noch mal ein kleines Beispiel für dich "bone", aber auch für andere Anfänger, geschrieben.
Sollte bei allen funktionieren hab extra keine Bilder oder so geladen:

Code: [AUSKLAPPEN]

;
; ======================= BeispielCode Blitz3D - (8.4.2007) =======================
; von LukasBanana :)
; besuch mal meine Homepage (die ist aber noch leider nicht ganz fertig):
; HLC Games: "http://www.hlc-games.de.vu"
;

; "Bevor ich anfange hier noch eine kleine Information:
; In diesem Beispiel Programm werde ich oft das Wort 'Global' verwaenden, ich weiss ja nicht ob du es schon kennts
; aber es schadet ja nichts wenn ich es die hier noch einem al erklaere. Wenn man in einer Funktion eine Variable
; definiert dann ist sie nicht Global weil sie nur inerhalb der Funktion bekannt ist! Sie wird in der Funktion erstellt
; und sobald die Funktion beendet wird werden alle darin definierten Variablen geloescht! Wenn man sie aber auserhalb der
; Funktion definiert und Global macht ('Global MeineVariable = 0') dann ist sie im ganzen Programm bekannt.
; Aber man sollte grundsaetzlich versuchen seine Programme so wenig wie moeglich Global zu halten!


; "Ich schreibe in zwischen meine Variablen auch nur noch auf Englisch da man in der Programmierung
; sowie so hauptsaechlich englisch spricth"

; -> Konstanten 'Screen Width' und 'ScreenHeight' definieren
; "Ich schreibe Konstanen in zwischen nur noch gross, (ist auch in C++ ueblich), damit man weiss das
; dies eine Konstante ist. Spaeter sieht man das sonst nicht mehr, vorallem in grossen Programmen"
Const SCR_WIDTH = 800, SCR_HEIGHT = 600, COLOR_DEPTH = 16

Graphics3D SCR_WIDTH, SCR_HEIGHT, COLOR_DEPTH, 1 ; -> 1 = VollbildModus (2 = FensterModus)
  AppTitle "BeispielCode", "Wollen Sie wirklich beenden?"

Gosub init   ; -> So viel wie moeglich in Unterprogramme oder Funktionen 'stecken', macht das programm
         ; uebersichtlicher und variabler"

Gosub functions ; "Das hier wuerde ich sogar eigentlich in eine extra 'include'- Datei packen aber hier
            ; mache ich einfach ein Unterprogramm"
; -> Apropo Funktionen: "Es ist am bessten wenn man so viel wie moeglich in Funktionen schreibt"
; In C++ gibt es sogar gar keine Unterprogramme die mit "Gosub" eingeleitet werden, es gibt
; nur noch Funktionen! Und man sollte so wenig wie moeglich 'Global' bleiben! Damit man sich mit den
; VariablenNamen nicht in die Quere kommt!

Gosub create_world   ; -> Die Welt wird erstellt, aber am bessten ist es wenn man sich eine 3D Engine baut
               ; um dann seine Welten nur noch mit einer Funktion wie z.B. "LoadMyWorld(file$)" zu laden.
               ; Aber fuer kleiner Beispiele lohnt es sich absolut nicht!

 ; ************************
Repeat

   Gosub calculate1

UpdateWorld
RenderWorld

   Gosub drawing

Flip

   Gosub calculate2

Until quit
 End
 ; ************************


.init

; GFX INIT

cam = CreateCamera()   ; "Ich schreibe meine Variablen allerdings offt relativ kurz (cam = Camera), doch nicht zu kurz
                  ; damit man spaeter noch weiss was es heissen soll. Oft drei Buschstaben, man sagt eh:
                  ; 'Programmierer sind grundsaetzlich faul'"
light = CreateLight()
TurnEntity light, 70, 0, 0 ; -> Licht etwas von der Seite kommen lassen

; VARIABLES

Const MOUSE_SPEED# = 0.25
Const COLLTYPE_ENTITY = 1, COLLTYPE_WALL = 2
Const GRAVITY# = -0.7

Global picked

; COLLISIONS

; -> Dies ist etwas komplizierter:
; COLLTYPE_ENTITY ist eine Konstante welches den ObjektTypen beschreibt, und COLLTYPE_WALL ist eine Konstante welche
; alle Objekte beschreibt mit denen ein Objekt des Typen COLLTYPE_ENTITY kollidiert.
; Ein 'COLLTYPE_ENTITY' Objekt kollidiert also von nun an mit allen 'COLLTYPE_WALL' Objekten!
; Die erste 2 bedeutet hier das es sich hier bei um eine 'ellipsoid-to-polygon collision' hadnelt
; (Bedauerlicher weise gibt es in Blitz3D noch keine 'polygon-to-polygon collisions'!)
; Die zweite 2 bedeutet dass das Objekt an den anderen Objekten 'abrutscht', dies ist bei SchwerkraftsProgrammen entscheident!
; 3 wuerde hier bedeuten dass sie nicht abrutschen, und 1 wurde bedeuten dass das Objekt bei einer Kollision anhaelt
; "(Was das bringt weiss ich auch nicht ^^)"
Collisions COLLTYPE_ENTITY, COLLTYPE_WALL, 2, 2

; "Man kann so viele Kollisionen beschreiben wie man nur will, ich habe in meinem groessten B3D-Projekt genau 25 Kollisionen,
; Und das sind noch nicht mal sehr viele"

Return

.functions

; Texturen

Const MAX_TEXTURES = 100

Dim hTex(MAX_TEXTURES)   ; -> HandleTexture Array (Dim erstellt einen sogenannten 'Array')
                  ; -> Loadtexture gibt eine zahl zurueck. Mit der nackten zahl selbst kann man nichts anfangen
                  ; diese zahl nennt man entweder 'ID' oder auch 'Handle' "zum Handeln"

Function Load_Texture(nr, file$)
   hTex(nr) = LoadTexture(file$)
   If hTex(nr) = False ; "Warum 'True' und 'False' schreiben wenn man auch 1 und 0 schreiben kann?
                  ; Das ist eine laengere geschichte, aber es ist besser wenn man mit 'True' und 'False' arbeitet!"
      RuntimeError "SCHLIMMER FEHLER!"+Chr(10)+Chr(10)+"Texture: "+Chr(34)+file$+Chr(34)+Chr(10)+"konnte nicht geladen werden!"
                  ; -> Chr(10) = Neue Zeile (ASCII Code), Chr(34) = Anfuehrungszeichen (")
   EndIf
End Function

; /Texturen

; Std Drawing

Function DrawPointer(x, y, r = 255, g = 255, b = 255) ; -> Alles wir din eine Funktion gesteckt!
                  ; "Da wir r, g und b schon einen Wert zugewiesen haben koennen wir uns beim Aufruf der
                  ; Funktion entscheiden ob wir diese Parameter nicht mehr angeben.
                  ; Wir koennen also schreiben: 'DrawPointer(x, y)' oder DrawPointer(x, y, r, g, b)"
   Color r, g, b
   Line(x-5, y  , x-15, y   )   ; -> Bei einigen Funktionen kann man die Klammern (, ) auch weglassen aber es schadet ja nichts
                        ; wenn man sie trotzdem hinschreibt
   Line(x+5, y  , x+15, y   )
   Line(x  , y-5, x   , y-15)
   Line(x  , y+5, x   , y+15)
   ; "Eigentlich wuerde ich diese Funktion noch variabler machen aber fuer`s Erste reicht das mal!"
End Function

; /Std Drawing

; PhysikEngine

; -> Es wird ein neuer Typ bzw. eine neue Struktur angelegt
; Der Typ heisst 'PhysikType' und hatt die Variablen 'entity' und 'gravity_speed#'
Type PhysikType
   Field entity, gravity_speed#
End Type

Function AddPhysikEntity(entity_)   ; "entity mit Unterstrich '_' damit wir in der Funktion die beiden verschiedenen Variablen
                           ; 'entity' und 'entity_' auseinander halten koennen"
   Phy.PhysikType = New PhysikType   ; -> Ganz entscheident: hier wir durch 'New' ein neues Objekt angelegt, dieses Objekt ist
                           ; Global, es ist also in allen Funktionen bekannt
   Phy\entity         = entity_
   Phy\gravity_speed   = GRAVITY
   ; ---
   EntityType Phy\entity, COLLTYPE_ENTITY ; "Hier kommt unsere Kollisionen zum Einsatz!"
End Function

; -------

Function PhysikEngine()
  For Phy.PhysikType = Each PhysikType   ; -> Auch dies ist ganz entscheident: Es werden alle Objekte vom Typ
                              ; 'PhysikType' 'durchlaufen' sodass wir sie alle bearbeiten koennen
   TranslateEntity Phy\entity, 0, Phy\gravity_speed, 0   ; -> 'TranslateEntity' ist so zu sagen eine Mischung aus
                                          ; 'MoveEntity' und 'PositionEntity'. Das Objekt wird wie bei
                                          ; 'MoveEntity' nur bewegt und nicht positioniert, aber wie bei
                                          ; 'PositionEntity' unabhaengig von der Rotation.
   If EntityCollided(Phy\entity, COLLTYPE_WALL)   ; -> Nun kommt die KollisionsAbfrage zum Einsatzt.
                                    ; Sobald das Objekt 'Phy\entity' auf ein Objekt vom Typen 'COLLTYP_WALL' auftrifft
      speed# = Phy\gravity_speed
      Phy\gravity_speed = -(speed * .7) ; -> Jetzt fliegt das Objekt wie nach oben, aber nicht mehr so schnell (* 0.7)
      TranslateEntity Phy\entity, 0, -speed, 0
   EndIf
   
   Phy\gravity_speed = Phy\gravity_speed - .025 ; -> Die Fallgeschwindigkeit soll immer mehr zu nehmen
   
   If picked = Phy\entity And MouseHit(1) Then Phy\gravity_speed = 2
   ; -> Wenn man das PhysikObjekt 'abschiesst' springt es wieder hoch
   
   ; Dies wurde absichtlich 'auskommentiert', aber wenn du spaeter Objekte Loeschen willst dann tu es so:
   If False ; -> Diese Abfrage wird niemals ausgefuehrt werden
      FreeEntity Phy\entity   ; -> Loescht das aktuelle Objekt vom Typ 'PhysikType'
      Delete Phy            ; -> Ebenfalls ganz entscheident: jetzt erst ist das Objekt an sich geloescht
   EndIf
  Next
End Function

; /PhysikEngine


Return

.create_world

; Himmel
sky = CreateCube()            ; "Ich verwaende erst mal keine Bilder usw. damit man dieses Programm ganz einfach startebn kann ^^"
EntityColor sky, 0, 100, 255   ; -> Der Himmel erhaelt eine hell blaue Farbe
ScaleEntity sky, 100, 100, 100   ; -> Der Himmel wird vergroessert um 100 auf 100 auf 100 Einheiten. Nicht Meter, nicht MilliMeter
                        ; sondern man nennt dies in der 3D Programmierung nur "Einheiten"
FlipMesh sky               ; -> Das Objekt wird 'umgekrempelt', so zu sagen ein NegativBild
EntityOrder sky, 1            ; -> Das Objekt ist immer im Hintergrund egal ob ein anderes Objekt versucht dur es hindurch zu
                        ; 'fallen'
EntityPickMode sky, 2         ; -> Jetzt kann das Objekt auch angepickt werden. 2 bedeutet hier, dass wir das Objekt
                        ; nach dem PolygonPickShema anpicken, dies ist das praktichste
EntityType sky, COLLTYPE_WALL   ; "Eigentlich hat ein Himmel keine Kollision aber es ist ja auch nur ein Beispiel Programm"

; Unser LaserPointer
laser = CreateSphere(5)         ; -> 5 bedeutet hier wie hoch die Kugel aufgeloest ist. von 2-100,
                        ; aber vorsicht bei 100 das kann schon dazu fuehren dass das Programm stottert!
EntityColor laser, 255, 0, 0   ; "Natuerlich einen roten Laserpointer"
ScaleEntity laser, 5, 5, 5

; Unser Ball
ball = CreateSphere(10)
EntityColor ball, 180, 160, 0
ScaleEntity ball, 7, 7, 7
EntityPickMode ball, 2
PositionEntity ball, 15, 70, 55      ; "Irgendeine Position"

AddPhysikEntity(ball)            ; "Wir fuegen es zu den PhysikObjekten hinzu"


Return

.calculate1

; -> Kamera Rotation
cam_pitch# = cam_pitch# + MouseYSpeed()*MOUSE_SPEED
cam_yaw# = cam_yaw# - MouseXSpeed()*MOUSE_SPEED
  If cam_pitch > 90 Then cam_pitch = 90      ; "Wir koennen ja schliesslich nicht mit unserem Kopf eine 360° Umdrehung machen!"
  If cam_pitch < -90 Then cam_pitch = -90
   RotateEntity cam, cam_pitch, cam_yaw, 0
   MoveMouse SCR_WIDTH/2, SCR_HEIGHT/2      ; -> Die Maus wird in der Mitte des Bildschirms positioniert damit wir
                                 ; uns endlos nach rechts und links drehen koennen!
                                 ; "Lasses ruihg mal weg und du wirst sehen was passiert ^^"

; "Unser Laserpointer"
picked = CameraPick(cam, SCR_WIDTH/2, SCR_HEIGHT/2)   ; "Wir definieren einen Picker der uns Objekte, die als anpickbares
                                       ; Objekt gelten, anpicken kann. Logisch oder?! Wir koennen damit also
                                       ; Durch die Funktionen 'Picked- X/ Y und Z' die X, Y und Z- Achse abfragen
                                       ; die wir zur Zeit anpicken. Dies wird immer verwaendet wenn im 'KillerSpielen'
                                       ; Jemand abgeschossen wird, es muss immer abgefragt werden ob ein Spieler
                                       ; grade angepickt wird, also los ..."

If picked Then PositionEntity laser, PickedX(), PickedY(), PickedZ()
; -> Der Laserpointer wird auf die angepickte Stelle positioniert
; -> mit der Variable 'picked' (eigentlich ist es ein Pointer, aber egal) koennen wir abfragen welches Objekt wir gerade anpicken
; Das kann z.B. so aussehen: 'If picked = sky Then ...' "Alles klar?! ^^"

; Schwerkraft
PhysikEngine()


Return

.drawing

Color 255, 255, 255
Text 0, 0, "BeispielCode - (8.4.2007)"

; "Ein Fadenkreutz macht sich immer gut! Und wie du siehst ist es in eine Funktion gepackt,
; wir muessen nur noch x, y und die Farbe angeben"
DrawPointer(SCR_WIDTH/2, SCR_HEIGHT/2, 200, 160, 0)

Return

.calculate2

; KEYBOARD

If KeyHit(1) Then quit = True
; -> Achtung: wenn man eine Taset drueckt, und dann erst spaeter eine Abfrage davon macht,
; dann hat sich das, das Blitz3DProgramm gemerkt und es findet eine Ausfuehrung statt.
; Also wenn man z.B. abfragt ob man die Taste 'Benutzen' in einem Spiel gedrueckt hat,
; und man drueckt diese Taste, aber es kommt erst spaeter zu einer Abfrage dann findet diese
; trotz verspaetung statt weil die Blitz3DProgramme sich die TastenDruecke merken!


Return


; -------------------------------------------------


Probiers mal aus und ändere einiges um zu sehen was passiert ^^

Gruß LukasBanana
Mein YouTube Channel: http://www.youtube.com/user/SoftPixel
Mein Twitter Profil: https://twitter.com/LukasBanana
Meine Projekte auf github.com: https://github.com/LukasBanana
Meine Projekte auf bitbucket.org: https://bitbucket.org/LukasBanana

LukasBanana

BeitragMi, Apr 11, 2007 18:47
Antworten mit Zitat
Benutzer-Profile anzeigen
Hat es denn so weit funktioniert? Ich hör ja gar nichts mehr, oder gibt`s keine fragen mehr?

Gruß LukasBanana
Mein YouTube Channel: http://www.youtube.com/user/SoftPixel
Mein Twitter Profil: https://twitter.com/LukasBanana
Meine Projekte auf github.com: https://github.com/LukasBanana
Meine Projekte auf bitbucket.org: https://bitbucket.org/LukasBanana

bone

BeitragDo, Apr 12, 2007 7:47
Antworten mit Zitat
Benutzer-Profile anzeigen
ja wa toll auch mit der dien komentaren foll nett vin dir danke es gibt sicher noch fragen aber jetzt noch nicht



MfG:bone

LukasBanana

BeitragDo, Apr 12, 2007 14:47
Antworten mit Zitat
Benutzer-Profile anzeigen
Fragen? Her damit ^^ (sollange ich sie beantworten kann)
Freut mich dass ich dir helfen konnte Very Happy

Gruß LukasBanana

;"It`s just a question of speed" << Lolliger Spruch, was ^^
Mein YouTube Channel: http://www.youtube.com/user/SoftPixel
Mein Twitter Profil: https://twitter.com/LukasBanana
Meine Projekte auf github.com: https://github.com/LukasBanana
Meine Projekte auf bitbucket.org: https://bitbucket.org/LukasBanana

bone

Betreff: ?????????

BeitragFr, Mai 11, 2007 16:55
Antworten mit Zitat
Benutzer-Profile anzeigen
ich habe mal ne frage ich mache gerade meine animierte maus wür mein spiel, beine bilder widerhollen sie so schnell das sie ausehen wie ein bild kann bir bitte eienr helfn !!!!!!!!!!!!!!






MfG: bone

BladeRunner

Moderator

BeitragFr, Mai 11, 2007 17:03
Antworten mit Zitat
Benutzer-Profile anzeigen
Würdest Du deine Frage bitte so formulieren dass sie verständlich ist ?

Wenn es darum gehen sollte dass die Animation zu schnell läuft empfehle ich mal die Forensuche mit Millisecs() und timer zu füttern, sollte eigentlich reichlich Ergebnisse liefern.
(Wohlmerklich erweiterte Suche -> alle Wörter suchen.)
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Abrexxes

BeitragFr, Mai 11, 2007 17:06
Antworten mit Zitat
Benutzer-Profile anzeigen
Hmmm...vielleicht will er seinen Bildern Beine machen...mystreiösle. ^^

bone

BeitragFr, Mai 11, 2007 20:22
Antworten mit Zitat
Benutzer-Profile anzeigen
hmmm geht nich und gefunde habe ich auch nits kann mir denn keiner hellfen ????
bittttte



MfG:bone

BladeRunner

Moderator

BeitragFr, Mai 11, 2007 21:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn Du dich dazu herablässt deine Sätze nochmal anzuschauen bevor du auf Absenden klickst und ordentlich zu schreiben dann würden Dir die Leute sicher lieber helfen.

Dass Du nichts gefunden hast mag ich kaum glauben. Immerhin habe ich mittels der erweiterten Suche (s.o.) zum Beispiel das hier gefunden:
https://www.blitzforum.de/foru...+millisecs
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

bone

BeitragSa, Mai 12, 2007 8:12
Antworten mit Zitat
Benutzer-Profile anzeigen
also ich habe eine animation die zu schnell leuft aber ich habe die animation in einem befehel (die animation is meine maus die ich machen) also kan ich zumbeispiel kein delay benutzen






HELP PLZ

MfG:bone

Silver_Knee

BeitragSa, Mai 12, 2007 11:11
Antworten mit Zitat
Benutzer-Profile anzeigen
was du gemacht hast(wahrscheinlich)
Code: [AUSKLAPPEN]
Repeat
...
frm=frm+1
Drawimage image,x,y,frm
...
until Keyhit (1)


frm=frm+1 soll allerdings langamer passieren also zb alle 100 millisekunden:

Code: [AUSKLAPPEN]
Repeat
...
If odtime+100<Millisecs();Erklärung unten
 frm=frm+1
 oldtime=Millisecs();ab jetzt zählt man die zeit
Endif
Drawimage image,x,y,frm
...
until Keyhit (1)


ein beisiel:
am anfang ist oldtime noch null und milisecs() die zeit in millisekunden seid windows gestartet ist zb 360000 (=6min) es heisst im If also:
If 0+100<360000
das is wahr
also
frm=frm+1
oldtime=Millisecs();also oldtime=360000
endif

zweiter durchlauf: in der zwischenzeit ist Millisecs()=360010 (10 millisekunden sind vergangen)
If 360000+100<360010
falsch es geht also weiter bis

Millisecs() ist inzwischen 360101 (also 101 millisekunden seid dem letztem mal frm=frm+1)
If 360000+100<360101 ;AHA kurz rechenen... wahr!
also frm=frm+1
und odtime=Millisecs()
und der ganze spaß geht von vorne los....

BladeRunner

Moderator

BeitragSa, Mai 12, 2007 17:56
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich gab Dir einen Linke der genau deine Frage beantwortet. Was willst du noch mehr, Bone ?
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Gehe zu Seite Zurück  1, 2

Neue Antwort erstellen


Übersicht BlitzBasic Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group