Wonderful 2D Water effects...

Game development specific discussions.
Post Reply
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Wonderful 2D Water effects...

Post by dodicat »

Don't do much of this game stuff, this is rapidly coded (white stretch) from some old stuff lying around doing nothing.
No mouse effects or anything like that.

Code: Select all



Extern "c"
Declare Function Transfer Alias "memcpy" (As Byte Ptr,As Byte Ptr,As Integer) As long
End Extern

'make a crude terrain
Sub terrain(xstart As Long,xend As Long,im As Any Ptr)
    Type _point
        As Single x,y
        As Ulong col
    End Type
    Dim As Long rotx,roty
    #macro rotate(pivotx,pivoty,px,py,a,scale)
    rotx=scale*(Cos(a*.0174533)*(px-pivotx)-Sin(a*.0174533)*(py-pivoty))+pivotx
    roty=scale*(Sin(a*.0174533)*(px-pivotx)+Cos(a*.0174533)*(py-pivoty))+pivoty
    #endmacro
    #define rr(first,last) Rnd * (last - first) + first
    #macro turnline(piv,p1,p2,ang,col,d)
    Scope
        rotate(piv.x,piv.y,p1.x,p1.y,ang,d)
        Var rot1=Type<_point>(rotx,roty)
        rotate(piv.x,piv.y,p2.x,p2.y,ang,d)
        Var rot2=Type<_point>(rotx,roty)
        Line im,(rot1.x,rot1.y)-(rot2.x,rot2.y),col
    End Scope
    #endmacro
    Dim As _point v1,v2,piv
    Dim As Ulong treecol
    Dim As Double pivx,pivy,pivz,l,k,d
    Dim As Long rd,g,b
    Var diff=0.0,delta=0.0
    Var yres=0.0
    For m As Double=0 To 50 Step 2'5
        Randomize m
        For n As Double=xstart-(m+rr(2,20)) To xend+m Step rr(3,9)
            Randomize n^2
            l=rr(4,11)
            k=rr(1,5)
            diff=m*2
            yres+=.02
            piv=Type(n,yres+diff*(1-Sin(.002*(n-m*5-k+40-100)))) '20
            Var cc=rr(1,40)
            Var yfin=450
            For a As Double=0 To yfin Step 7
                Randomize a
                Var shader=rr(1,6)
                rd=20+shader+cc
                g=150+shader:If g>40 Then g=g-40
                b=20+shader:If b>20 Then b=b-20
                treecol=Rgb(rd/2,g/2,b/2)
                For a2 As Double=0 To l Step .3
                    If a>270 Then shader=-shader
                    treecol=Rgb(rd,(g-a2*shader)/2,b)
                    v1=Type(piv.x-a2,piv.y)
                    v2=Type(piv.x-l,piv.y)
                    turnline(piv,v1,v2,a,treecol,1)
                Next a2
            Next a
        Next n
    Next m
End Sub

'===== plotting ======
#macro ppset(_x,_y,colour)
pixel=row+pitch*(_y)+4*(_x)
*pixel=(colour)
#endmacro

Dim As Integer Xres,Yres
Dim Shared As integer pitch
Dim Shared As any Pointer row
Dim Shared As Uinteger Pointer pixel
Screenres 900,600,32
Screeninfo Xres,Yres
'=================================


Function Regulate(Byval MyFps As Long,Byref fps As Long) As Long
    Static As Double timervalue,lastsleeptime,t3,frames
    Var t=Timer
    frames+=1
    If (t-t3)>=1 Then t3=t:fps=frames:frames=0
    Var sleeptime=lastsleeptime+((1/myfps)-T+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    lastsleeptime=sleeptime
    timervalue=T
    Return sleeptime
End Function

'colours for ripples
Const col1=Rgba(0,155,255,50)
Const col2=Rgba(0,100,255,50)
Const col3=Rgba(255,255,255,155)
'flow macro
#macro Flow(p)
For z As Integer= (size shr 2) to 0 step -1
    Swap p[z],p[z+1]
Next z
#endmacro

Function water(iwidth As Long=0,iheight As Long=0) As Any Ptr
    #define Intrange(f,l) Int(Rnd*((l+1)-(f))+(f))
    Dim  As Double N1=.1,N2=0
    Static As Byte B1(),B2(),runflag
    Static As Any Ptr im
    Static As Long _iheight,_iwidth
    'dim as long size
    #macro Copy()
    Transfer(@B1(0,0),@B2(0,0),(_iwidth+3)*(_iheight+3))
    #endmacro
    If runflag=0 Then
        runflag=1
        _iwidth=iwidth:_iheight=iheight
        im=Imagecreate(iwidth,iheight)
        Imageinfo im,,,,pitch,row
        Redim B1(iwidth+2,iheight+2)
        Redim B2(iwidth+2,iheight+2)  
        For n1 As Long=0 To iwidth+2
            For n2 As Long=0 To iheight+2
                b1(n1,n2)=IntRange(0,2)
                b2(n1,n2)=IntRange(0,2)
            Next
        Next
    End If
    'Screenlock
	For y As Integer= 1 To _iheight
		For x As Integer= 1 To _iwidth
			If B1(x,y)=0 And N1>Rnd Then B2(x,y)=1
			If B1(x,y)=2 Then B2(x,y)=0
			If B1(x,y)=1 Then
				B2(x,y) = 1
				If B1(x-1,y-1)=2 Or B1(x,y-1)=2 Or B1(x+1,y-1)=2 Then B2(x,y)=2
				If B1(x-1,y)=2 Or B1(x+1,y)=2 Or N2>Rnd Then B2(x,y)=2
				If B1(x-1,y+1)=2 Or B1(x,y+1)=2 Or B1(x+1,y+1)=2 Then B2(x,y)=2
            End If
			If B2(x,y)=0 Then:ppset((x-1),(y-1),col1):End If
			If B2(x,y)=1 Then:ppset((x-1),(y-1),col2):End If
			If B2(x,y)=2 Then:ppset((x-1),(y-1),col3):End If
        Next x
    Next y
    water=im
    'Screenunlock
	Copy()
End Function

Dim As Long ctr,fps
locate 10,10
print "terraforming ... "
'setup water and image
water(xres,300)'water image size
Dim As Any Ptr Image=Imagecreate(xres,300,Rgb(255,255,255))'same as water image
dim as ulong ptr pi:dim as integer size
Imageinfo image,,,,,pi,size
'=============================
'setup a background
Dim As Any Ptr trn=Imagecreate(xres,yres)
terrain(0,xres,trn)'north
For n As Long=0 To xres
    Var k=yres-200
    var g=100*sin(n/(xres/2))+20
    Line trn,(n,yres)-(n,k-120*Sin(n/xres+.0075*(cos(n/2)))),Rgb(g,g,g) 'south
Next

Do
    ctr+=1
    If ctr Mod 4=0 Then Put image,(0,0),water,Alpha:ctr=0
     if ctr mod 2 =0 then :Flow(pi):end if
    Screenlock
    'Cls  'optional
   
    Put(0,100),image,Pset 'water
    Put(0,0),trn,trans  'background
    Locate 1,1
    Print fps
    Screenunlock
    Sleep regulate(50,fps),1
Loop Until Len(Inkey)
'Wend
Sleep
'Imagedestroy im 
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: Wonderful 2D Water effects...

Post by dafhi »

i have been super busy. but i got terrain working :D

[updated]

Code: Select all

/' --- 2016 Aug 31
  fixed crash when aquarect initialized w/o terrain
'/


' ---- imagevars holds image metrics.  It can mostly be ignored ;)
'
type imagevars  ' 2016 Aug 30  by dafhi
  as integer    w,h,bpp,bypp,pitch,rate, is_screen
  as single     wm,hm,midx,midy
  as any ptr    im,pixels
  declare sub   getinfo(im as any ptr=0)
  declare sub   screen_init(w as short, h as short)
  declare sub   create(w as integer=0, h as integer=0, col as ulong=&HFFFF00FF)
  declare constructor(w as short=0, h as short=0, col as ulong=&HFFFF00FF)
  declare destructor
 private:
  declare sub   release
  as any ptr    hRelease
End Type
sub imagevars.release '2016 Aug 30
  w=0: h=0: bpp=0: bypp=0: im=0: pixels=0
  If ImageInfo(hRelease) = 0 Then ImageDestroy hRelease:  hRelease = 0
End Sub

Destructor.imagevars: release: End Destructor

constructor imagevars(w as short, h as short, col as ulong) ' 2016 Aug 30
  if w<1 or h<1 then exit constructor
  if screenptr = 0 then:  screen_init w,h
  else:  create w,h
  EndIf
End Constructor

sub imagevars.getinfo(im as any ptr)
  release
  if im=0 then
    ScreenInfo w,h, bpp, bypp, pitch, rate:  pixels=screenptr
    is_screen = -1: im=0
  elseif Imageinfo(im)=0 then
    ImageInfo im, w, h, bypp, pitch, pixels:  bpp = bypp * 8
    this.im = im:  is_screen = 0
  endif: hRelease = im
  wm=w-1:  midx=w/2
  hm=h-1:  midy=h/2
end sub
sub imagevars.screen_init(w as short, h as short):
  screenres w,h,32: getinfo
End Sub
sub imagevars.create(w as integer, h as integer, col as ulong)
  getinfo:  if bypp = 0 then exit sub
  getinfo imagecreate(w,h,col)
End Sub


' --- UDT for independent anim / physics fps -  2016 Aug 29 - by dafhi

' dim as gtPhysAni  anim            '
' do                                '
'   gt = Timer  'global time        '
'   if anim.rdy2draw then           '
'     cls                           '  --- code sample ---
'     ? "fps "; anim.fps_report     '
'   EndIf                           '
'   x += dx * anim.phys_frames      '
'   angle += anim.phys(3.14159)     '

dim shared as double      gt  'global time

type gtPhysAni
  as single               anim_fps = 60   'set these to whatever you want
  as single               phys_fps = 243  '
  as string               fps_report
  declare function        rdy2draw as boolean
  declare function        phys_frames as integer
  declare function        phys(unit_per_sec as double = 1) as double
  as double               tp, td, iphys
  private:
  as double               phys_tp, anim_td, report_tp
End Type
function gtPhysAni.phys(unit_per_sec as double) as double
  return phys_frames * unit_per_sec * iphys
End Function
function gtPhysAni.phys_frames as integer
  if phys_tp = 0 then phys_tp = timer
  var frames = int(phys_fps * (gt - phys_tp) + 0.5)
  iphys = 1 / phys_fps:  phys_tp += frames * iphys:  return frames
End Function
function gtPhysAni.rdy2draw as boolean
  if tp = 0 then tp = gt - 1.01/anim_fps: report_tp = tp
  td = gt - tp:  anim_td += td
  tp = gt
  if anim_td >= 1 / anim_fps then
    static as integer fps_frames:  fps_frames += 1
    if gt-report_tp >= 1 then '2016 Aug 29
      fps_report = str( int( 10*fps_frames/(gt-report_tp)+0.5 ) / 10)
      report_tp = gt
      fps_frames = 0
    EndIf:  anim_td -= (1/anim_fps) * int(anim_td * anim_fps)
    return true
  EndIf:  return false
End function

'return a range-confined value
function constrain(in as double, lo as double, hi as double) as double
  if in<lo then: in=lo
  elseif in>hi then: in=hi
  endif: return in
end function


' for palette of greenish blues...
dim shared as ulong pal(255)
' to make a palette between two color values: 007B91 to 68B8FF
' r = 0 to &h68
' g = &h7B to &hB8
' b = &h91 to &hFF
' interval = ((hi - lo)/255)
for p as integer = 0 to 255
    dim as ubyte r =    0 + (&h68 -    0)/255 * p
    dim as ubyte g = &h7B + (&hB8 - &h7B)/255 * p
    dim as ubyte b = &h91 + (&hFF - &h91)/255 * p
    pal(p) = rgb(r,g,b)
next p


' --- aquarect ----
'
const AA_BORDER = 1 'New as of Aug 28.  Sub-sampling.
     
type tULDR          '32 bytes per pixel ftw
  as long           u,l,d,r     'offsets
  as single         su,sl,sd,sr 'sub-pixel alphas
End Type

type  aquarect
  as integer        w,h, wm, hm '
  as ulong          water_mask  'for user reference
  declare sub       render_target(byref pdest as imagevars ptr)
  declare sub       render(posx as short=0, posy as short=0)
  declare sub       define_flow(w as integer=256, h as integer=256, byref pTerrain as imagevars ptr=0, water_mask as ulong=&HFF00FF)
  declare sub       put_pixel(x as ushort=0, y as ushort=0,val as single=0)
  declare constructor(w as short=0, h as short=0, byref pTerrain as imagevars ptr=0, water_mask as ulong=&HFF00FF)
 private:
  declare sub       to_abs(byref ret_int as long, _
                           byref ret_sng as single, _
                           sval as single, _
                           ref_pixel_index as long, _
                           x_or_y as integer, _
                           wm_or_hm as integer, _
                           pitch as integer)
                           
  declare sub       process
  as imagevars ptr  pimvdest, pTerr
  as single ptr     psngdest, psrc
  as integer        ub1d
  as ulong ptr      pixT
  as single         AquaSng0(any), AquaSng1(any)
  as tULDR          flow(any)
  as tULDR ptr      pFlow
  as boolean        valid_terrain
End Type
constructor aquarect(w as short, h as short, byref pTerrain as imagevars ptr, water_mask as ulong)
  if w=0 or h=0 then exit constructor
  define_flow w,h, pTerrain
End Constructor
sub aquarect.render_target(byref pdest as imagevars ptr)
  pimvdest=pdest
End Sub
sub aquarect.put_pixel(x as ushort, y as ushort,val as single)
  psrc[x+y*w]=val
End Sub
sub aquarect.to_abs(byref ret_int as long, _
                    byref ret_sng as single, _
                    uldr as single, _
                    ref_pixel_index as long, _
                    x_or_y as integer, _
                    wm_or_hm as integer, _
                    pitch as integer)
                   
  var within_bounds = int(constrain( uldr, -x_or_y + AA_BORDER, wm_or_hm - x_or_y - AA_BORDER))
  ret_int = ref_pixel_index + within_bounds * pitch
  ret_sng = uldr-int(uldr)
End Sub
sub aquarect.define_flow(w as integer, h as integer, byref pTerrain as imagevars ptr, _water_mask as ulong)

  ub1d = w*h-1
  redim AquaSng0(ub1d)    '1d arrays for graphics .. yes!
  redim AquaSng1(ub1d)
  redim flow(ub1d)
 
  pSngDest = @AquaSng1(0)
  psrc = @AquaSng0(0)
  pflow = @flow(0)

  wm=w-1:  this.w=w
  hm=h-1:  this.h=h
 
  valid_terrain = false
  if pTerrain <> 0 then
    valid_terrain = pTerrain->w=w and pTerrain->h=h
    if valid_terrain then
      water_mask = _water_mask and &HFFFFFF
      pTerr = pTerrain
      pixT = pTerr->pixels
    endif
  endif

   
  var uu = .14
  var dd = -.14
  var ll = -.14
  var rr = .14

  dim as single u,d,l,r

  for y as integer = 0 to hm
    if valid_terrain then
    
      for x as integer= 0 to wm
        var i = y*w+x
        if (pixT[i] and &HFFFFFF) = water_mask then
          pixT[i] = water_mask 'alpha to match for quick calc in render()
          u=uu:d=dd:l=ll:r=rr
          if x<wm then if (pixT[i+1] and &HFFFFFF) <> water_mask then r=-r
          if y<hm then if (pixT[i+w] and &HFFFFFF) <> water_mask then u=-u
          if x>0 then if (pixT[i-1] and &HFFFFFF) <> water_mask then l=-l
          if y>0 then if (pixT[i-w] and &HFFFFFF) <> water_mask then d=-d
          to_abs pFlow[i].l, pFlow[i].sl, l, i, x, wm, 1
          to_abs pFlow[i].r, pFlow[i].sr, r, i, x, wm, 1
          to_abs pFlow[i].u, pFlow[i].su, u, i, y, hm, w
          to_abs pFlow[i].d, pFlow[i].sd, d, i, y, hm, w
        endif
      Next

    else 'all water
     
      for x as integer= 0 to wm
        var i = y*w+x
        u=uu:d=dd:l=ll:r=rr
        to_abs pFlow[i].l, pFlow[i].sl, l, i, x, wm, 1
        to_abs pFlow[i].r, pFlow[i].sr, r, i, x, wm, 1
        to_abs pFlow[i].u, pFlow[i].su, u, i, y, hm, w
        to_abs pFlow[i].d, pFlow[i].sd, d, i, y, hm, w
      Next

    endif
  next
 
End Sub

sub aquarect.render(x as short, y as short)
  if pimvdest=0 or pimvdest->pixels = 0 then exit sub
 
  dim as tULDR ptr  pflow = @flow(0)
 
  for y as integer = 0 to hm 'water buffers (single precision)
    var x=y*w
    for x=x to x+wm
      var l = pflow[x].l
      var u = pflow[x].u
      var d = pflow[x].d
      var r = pflow[x].r
      psngdest[x]=( psrc[l] + pflow[x].sl * ( psrc[l+1] - psrc[l] ) + _
                    psrc[u] + pflow[x].su * ( psrc[u+w] - psrc[u] ) + _
                    psrc[d] + pflow[x].sd * ( psrc[d+w] - psrc[d] ) + _
                    psrc[r] + pflow[x].sr * ( psrc[r+1] - psrc[r] )) / 2.0001 - psngdest[x]
      if psngdest[x]<0 then: psngdest[x]=0
      elseif psngdest[x]>2.7 then: psngdest[x]=2.7
      endif
    next
  Next:  swap psngdest, psrc
 
  'water to image
  dim as ulong ptr  _dst = pimvdest->pixels
  dim as single ptr src = psrc
 
  _dst -= x*(x>0) + pimvdest->w*y*(y>0)
  src += x*(x<0) + w*y*(y<0)
  var clipwm = (x+wm-pimvdest->wm)*((x+wm)>pimvdest->wm) + wm-x*(x<0)
  var cliphm = (y+hm-pimvdest->hm)*((y+hm)>pimvdest->hm) + hm-y*(y<0)

  if valid_terrain then
    dim as ulong ptr  pT = pixT: pT += src-psrc
    for dst as ulong ptr = _dst to @_dst[cliphm*pimvdest->w] step pimvdest->w
      for i as integer = 0 to clipwm
        if pT[i] = water_mask then
          dim as integer a = src[i]*255.499
          dst[i] = pal(a and &HFF)
        else
          dst[i] = pT[i]
        EndIf
      next: src += w: pT += w
    next
  else
    for dst as ulong ptr = _dst to @_dst[cliphm*pimvdest->w] step pimvdest->w
      for i as integer = 0 to clipwm
        dst[i] = pal(src[i]*255.499)
      next: src += w
    next
  EndIf
End Sub


Sub Main

  dim as imagevars    buf=type(800, 600) 'ScreenRes called if uninitialized gfx

  dim as ulong        water_mask = &HffFF00FF
  dim as imagevars    map=type(480, 360)
 
  circle map.im, (map.midx+100, map.midy), 50, &HFFFF00, , , 1.0', f
  line map.im, (map.w/5, map.h/2)-(map.w/2, map.h*0.75),,b

  dim as aquarect     my_aqua=type(map.w, map.h, @map, water_mask)

  my_aqua.render_target @buf

  dim as gtPhysAni    fps
  fps.phys_fps = 13

  do
    gt = timer
    for i as integer = 1 to fps.phys_frames
      my_aqua.put_pixel int(rnd*my_aqua.w), int(rnd*my_aqua.h), rnd*1.5
    Next
    if fps.rdy2draw then
      screenlock
        cls
        my_aqua.render 10,10
        ? "fps: "; fps.fps_report
      screenunlock
    endif
    sleep 1
  loop until inkey = chr(27)
end sub

Main
Last edited by dafhi on Aug 31, 2016 19:55, edited 2 times in total.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Wonderful 2D Water effects...

Post by leopardpm »

impressive!

now I need to unwrap all that to understand what you have done, in both of your last two posts. I think I am seeing the ripples 'reflect' off the edges of the shapes drawn, at least on the inner parts - can't notice so much on the outside of the shapes. That is a nice effect!

I like your water movement on the post with using tiles (I am assumng you used tiles, haven't checked yet...)
nope, not tiles at all - but having issue understand what you are doing to create the water image... very interesting!
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: Wonderful 2D Water effects...

Post by dafhi »

1. longer-lasting waves in .render()
psrc[r] + pflow[x].sr * ( psrc[r+1] - psrc[r] )) / 2.0001 - psngdest[x]

this here is linear interpolation -> psrc[r] + pflow[x].sr * ( psrc[r+1] - psrc[r] )

2. increase the water max for some contrast
elseif psngdest[x]>2.7 then: psngdest[x]=2.7

3. slower-moving waves .define_flow()
var u = .14
var d = -.14
var l = -.14
var r = .14
Last edited by dafhi on Aug 30, 2016 16:18, edited 1 time in total.
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: Wonderful 2D Water effects...

Post by dafhi »

more info about my sampling technique. so the original author uses 4 sample points (u,l,d,r)
and i decided to do some anti-aliasing. I use 4 sample pairs, so 8 points in total.

my u and d are vertical pairs .. u+w represents the pixel above 'u'

Code: Select all

psrc[u] + pflow[x].su * ( psrc[u+w] - psrc[u] )
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Wonderful 2D Water effects...

Post by leopardpm »

BC - in thinking about this pathing issue within the context of a Tower Defense game, the same problem crops up that I was having in trying to get the river water flowing correctly around corners... the problem stems from the fact that the 'shortest path' is through the point closest to the corner, so, in the Tower Defense game, all the units may start out on a wide road at different points, but they will all path the the first corner and from there they will all follow the exact same path! Yuck! One possible solution I thought of (in respect to tower Defense), is to make multiple (5+ ?) pathing lanes which units follow... but they would bunch up through bottlenecks.. don't know about how to get them back onto their respective paths after a bottleneck...

Image
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Wonderful 2D Water effects...

Post by BasicCoder2 »

Where the three paths appear to converge it would simply be three different paths having the same values. Following the path each cycle would not be fixed to one step per cycle. To take the next position would require that there was no other agent blocking it or enemy to fight. I haven't coded more than one agent so far but I suspect it would be like the Soccer game I wrote where all agents were trying to get to the same ball. The presence of an enemy would change the goal to fighting the enemy. If not killed it would resume following the path.

With regards to flowing water, in the real world the water particles speed up as the river's width/depth reduces. Or the water particles in front of the narrow path would slow down due to back pressure. Water particles are simply like agents (particle size). The waves are just the particles connected by "springs" moving up and down transferring their kinetic energy from particle to particle. The math behind modeling all that is beyond my current know how.
.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Wonderful 2D Water effects...

Post by leopardpm »

BasicCoder2 wrote:Where the three paths appear to converge it would simply be three different paths having the same values. Following the path each cycle would not be fixed to one step per cycle.
Yup, that is what I came up with as well - a different influence map for each lane, and each new agent is assigned a lane (which they can change if needed or desired). I also figured out a way to determine the center of each of these lanes through also using multiple breadth-first flood fills.

(Note: I use the terms "influence map", "flow map", "breadth-first fill" all interchangeably since they are all the same thing, created in the same manner)
To take the next position would require that there was no other agent blocking it or enemy to fight.
I am assuming no need to do collisions between agents, but it might lead to some more strategic gameplay...

as for fighting, I was thinking each agent would have some sort of 'Aggro' distance (I use the term aggro because that is the common reference in all games) which is the distance between units (of opposing factions) at which they take notice of each other and go into attack mode. So, with each frame (or more), check the distances between all the agents to determine if they should go into attack mode. I am sure there are ways to optimize this greatly.
I haven't coded more than one agent so far but I suspect it would be like the Soccer game I wrote where all agents were trying to get to the same ball. The presence of an enemy would change the goal to fighting the enemy. If not killed it would resume following the path.
yes
With regards to flowing water, in the real world the water particles speed up as the river's width/depth reduces. Or the water particles in front of the narrow path would slow down due to back pressure. Water particles are simply like agents (particle size). The waves are just the particles connected by "springs" moving up and down transferring their kinetic energy from particle to particle. The math behind modeling all that is beyond my current know how.
yup, but as far as a relatively 'realistic' simulation of water flowing, it could be also based on the multiple lane pathing approach, as the number of lanes increase, the realism increases, towards the maximum realism that this method could produce. It would not simulate water speed, though each lane could have a 'speed' based on distance from the center (water nearer to the edge would flow slower) and even depth (each lane could be assigned an average depth which could influence speed). If one wanted to get extra fancy, then also keep track of the slope of the terrain under the water which could be another additive to the water speed. Once all these things are combined, you would get some pretty good looking simulation of river water flowing! But, I think that sure is alot of work for not much result, too complex for the basic game, especially considering the decent looking alternatives that have been presented here by others.

I like the way this Tower Defense pathing is going - I will code up my ideas for analyzing a given map and determining the influence maps for each of the pathing lanes and make a new thread - maybe it will turn into a little game at some point! It would be nice to be able to take any given handdrawn map and have the program be able to figure out any and all pathing within it.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Wonderful 2D Water effects...

Post by leopardpm »

oops... didn't think about the potential of path splitting into 2 or more different directions - as your initial program shows... drats - that could make things much trickier to figure out....hmmmm
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Wonderful 2D Water effects...

Post by dodicat »

Here is some water following a path.
O.K. here with -gen gas.
64 bit, as usual, is crap at direct pixel drawing.
32 bit -gen gcc is a bit slowed down (again as per usual for graphics)

Code: Select all


#include "crt.bi"
#define Transfer memcpy
'===== plotting ======
#macro ppset(_x,_y,colour)
pixel=row+pitch*(_y)+4*(_x)
*pixel=(colour)
#endmacro

Dim As Integer Xres,Yres
Dim Shared As Integer pitch
Dim Shared As Any Pointer row
Dim Shared As Uinteger Pointer pixel
Screen 20,32
Screeninfo Xres,Yres
'=================================

'colours for ripples
Const col1=Rgba(0,155,255,50)
Const col2=Rgba(0,100,255,50)
Const col3=Rgba(255,255,255,155)
'flow macro
#macro Flow(p)
For z As Integer= (size Shr 2) To 0 Step -1
    Swap p[z],p[z+1]
Next z
#endmacro
Function water(iwidth As Long=0,iheight As Long=0) As Any Ptr
    #define Intrange(f,l) Int(Rnd*((l+1)-(f))+(f))
    Dim  As Double N1=.1,N2=0
    Static As Byte B1(),B2(),runflag
    Static As Any Ptr im
    Static As Long _iheight,_iwidth
    'dim as long size
    #macro Copy()
    Transfer(@B1(0,0),@B2(0,0),(_iwidth+3)*(_iheight+3))
    #endmacro
    If runflag=0 Then
        runflag=1
        _iwidth=iwidth:_iheight=iheight
        im=Imagecreate(iwidth,iheight)
        Imageinfo im,,,,pitch,row
        Redim B1(iwidth+2,iheight+2)
        Redim B2(iwidth+2,iheight+2)  
        For n1 As Long=0 To iwidth+2
            For n2 As Long=0 To iheight+2
                b1(n1,n2)=IntRange(0,2)
                b2(n1,n2)=IntRange(0,2)
            Next
        Next
    End If
    'Screenlock
	For y As Integer= 1 To _iheight
		For x As Integer= 1 To _iwidth
			If B1(x,y)=0 And N1>Rnd Then B2(x,y)=1
			If B1(x,y)=2 Then B2(x,y)=0
			If B1(x,y)=1 Then
				B2(x,y) = 1
				If B1(x-1,y-1)=2 Or B1(x,y-1)=2 Or B1(x+1,y-1)=2 Then B2(x,y)=2
				If B1(x-1,y)=2 Or B1(x+1,y)=2 Or N2>Rnd Then B2(x,y)=2
				If B1(x-1,y+1)=2 Or B1(x,y+1)=2 Or B1(x+1,y+1)=2 Then B2(x,y)=2
            End If
			If B2(x,y)=0 Then:ppset((x-1),(y-1),col1):End If
			If B2(x,y)=1 Then:ppset((x-1),(y-1),col2):End If
			If B2(x,y)=2 Then:ppset((x-1),(y-1),col3):End If
        Next x
    Next y
    water=im
    'Screenunlock
	Copy()
End Function

Sub rotate(im As Any Ptr,angle As Single,shiftx As Integer=0,shifty As Integer=0)
    #define InRange() resultx>=0 And resultx<ddx And resulty>=0 And resulty<ddy And _
    x+shiftx>=0 And x+shiftx<xres And y+shifty>=0 And y+shifty<yres
    Dim As Integer pitch,pitchs,xres,yres
    Dim As Any Ptr row
    Dim As Uinteger Ptr pixel,pixels
    Dim As Integer ddx,ddy
    Imageinfo im,ddx,ddy,,pitch,row
    Screeninfo xres,yres,,,pitchS
    Dim As Any Ptr rowS=Screenptr
    Dim As Integer centreX=ddx\2,centreY=ddy\2
    Dim As Single sx=Sin(angle)
    Dim As Single cx=Cos(angle)
    Dim As Integer mx=Iif(ddx>=ddy,ddx,ddy)
    Var fx=Sqr(2)/2
    For y As Integer=centrey-fx*mx To centrey+ fx*mx Step 1
        For x As Integer=centrex-mx*fx To centrex+mx*fx Step 1
            Dim As Integer resultx=(Cx*(x-centrex)-Sx*(y-centrey)) +centrex,resulty=(Sx*(x-centrex)+Cx*(y-centrey)) +centrey
            If InRange() Then
                pixel=row+pitch*((resulty))+((resultx)) Shl 2
                pixels=rowS+pitchS*(y+shifty)+(x+shiftx) Shl 2
                *pixels=*pixel
            End If
        Next x
    Next y
End Sub

Function Regulate(Byval MyFps As Long,Byref fps As Long) As Long
    Static As Double timervalue,lastsleeptime,t3,frames
    Var t=Timer
    frames+=1
    If (t-t3)>=1 Then t3=t:fps=frames:frames=0
    Var sleeptime=lastsleeptime+((1/myfps)-T+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    lastsleeptime=sleeptime
    timervalue=T
    Return sleeptime
End Function

Dim As Long ctr,fps
'setup water and image
water(120,120)'water image size
Dim As Any Ptr Image=Imagecreate(120,120,Rgb(255,255,255))'same as water image
Dim As Ulong Ptr pi:Dim As Integer size
Imageinfo image,,,,,pi,size
Dim As Any Ptr trn=Imagecreate(xres,yres)
'=============================
'make the river course ========================
Type pt
    As Long x,y
End Type
Dim As pt P1(1 To 24)
Dim As pt P2(1 To 24)
For n As Long=1 To 24
    Read P1(n).x
Next
For n As Long=1 To 24
    Read P2(n).x
Next
Restore Y_values
For n As Long=1 To 24
    Read P1(n).y
Next
For n As Long=1 To 24
    Read P2(n).y
Next
For n As Long=1 To 24-1
    Line trn,(P1(n).x,P1(n).y)-(P1(n+1).x,P1(n+1).y),Rgb(255,255,255)
    Line trn,(P2(n).x,P2(n).y)-(P2(n+1).x,P2(n+1).y),Rgb(255,255,255)
Next
Paint trn,(900,100),Rgb(0,100,0),Rgb(255,255,255)
Paint trn,(100,600),Rgb(0,100,0),Rgb(255,255,255)
'====================================================
Do
    ctr+=1
    '=================  Water to one image only ===========
    If ctr Mod 4=0 Then Put image,(0,0),water,Alpha:ctr=0
    If ctr Mod 2 =0 Then :Flow(pi):End If
    '===============================================  
    Screenlock
    'Cls  'optional
    'now slide the image along the riverbank, rotating round the bends
    For n As Long=1 To 24-1
        Var a=Atan2((p1(n+1).y-p1(n).y),(p1(n+1).x-p1(n).x))
        If n<16 Then rotate(image,-a,p1(n).x-50,p1(n).y-50)
        If n>=16 And n<=19 Then rotate(image,-a,p2(n).x-20,p2(n).y-50)
        If n>19  Then rotate(image,-a,p1(n).x-65,p1(n).y-50)
    Next
    Put(0,0),trn,trans  'background
    Locate 1,1
    Print fps
    Screenunlock
    Sleep regulate(50,fps),1
Loop Until Len(Inkey)
sleep 100
print "Press a key to end"
Sleep
if image <> 0 then Imagedestroy image
if trn <> 0 then Imagedestroy trn
screen 0
X_values:

Data _
0, 114, 176, 237, 247, 300, 360, 447, 512, 568, 591, 628, 688, 788, 838, 859, 853, 839, 807, 801, 888, 960, 1063, 1150, 0, 98, 143, 187, 195, 245, 326, 446, 532, 595, 622, 654, 688, 769, 798, 796, 790, 776, 736, 738, 830, 951, 1063, 1151 

Y_values:

Data _
100, 118, 147, 224, 282, 342, 370, 366, 337, 279, 219, 150, 117, 135, 222, 316, 372, 439, 535, 595, 664, 695, 712, 721, 154, 160, 181, 231, 282, 351, 398, 406, 373, 295, 226, 172, 142, 150, 224, 317, 364, 429, 518, 599, 687, 733, 760, 770 

 
Of course, the flow rate quite fast for the demo.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Wonderful 2D Water effects...

Post by leopardpm »

dodicat wrote:Here is some water following a path.
whooooa! This is nice! Do the river bank edges have to be polygonal(I see no curves, just straight lines), or can the river banks be 'hand drawn' and irregular?

Don't have time right now to study your code, but I think you have it, at least enough realism for most 2D games, if not more! This will be interesting to see how you went around the corners...
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Wonderful 2D Water effects...

Post by dodicat »

I could quite easily draw a Catmul rom spline through the points for a smooth effect.
I'll run that through in a bit.
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: Wonderful 2D Water effects...

Post by srvaldez »

hello dodicat
the program crashes on my PC if the program runs for about one minute, also crashes on exit, I suspect memory corruption.
but that aside, I get about 17 fps with fb x64 -gen gcc -Wc -O2 but get about 43 fps with -Ofast
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Wonderful 2D Water effects...

Post by dodicat »

Here is a spline along each riverbank.
Srvaldez:
I have altered a size for memcpy, maybe that was the cause for crashing.
Seems ok here, I ran the thing for about 20 minutes, no hiccups.

Code: Select all



#include "crt.bi"
#define Transfer memcpy
'===== plotting ======
#macro ppset(_x,_y,colour)
pixel=row+pitch*(_y)+4*(_x)
*pixel=(colour)
#endmacro

Dim As Integer Xres,Yres
Dim Shared As Integer pitch
Dim Shared As Any Pointer row
Dim Shared As Uinteger Pointer pixel
Screen 20,32
Screeninfo Xres,Yres
'=================================

'colours for ripples
Const col1=Rgba(0,155,255,50)
Const col2=Rgba(0,100,255,50)
Const col3=Rgba(255,255,255,155)
'flow macro
#macro Flow(p)
For z As Integer= (size Shr 2) To 0 Step -1
    Swap p[z],p[z+1]
Next z
#endmacro
Function water(iwidth As Long=0,iheight As Long=0) As Any Ptr
    #define Intrange(f,l) Int(Rnd*((l+1)-(f))+(f))
    Dim  As Double N1=.1,N2=0
    Static As Byte B1(),B2(),runflag
    Static As Any Ptr im
    Static As Long _iheight,_iwidth
    static as long size
    #macro Copy()
    Transfer(@B1(0,0),@B2(0,0),size)'(_iwidth+3)*(_iheight+3))
    #endmacro
    If runflag=0 Then
        runflag=1
        _iwidth=iwidth:_iheight=iheight
        im=Imagecreate(iwidth,iheight)
        Imageinfo im,,,,pitch,row
        Redim B1(iwidth+2,iheight+2)
        Redim B2(iwidth+2,iheight+2)
        size=(ubound(B1,1)-lbound(B1,1)+1)*(ubound(B1,2)-lbound(B1,2)+1)
        For n1 As Long=0 To iwidth+2
            For n2 As Long=0 To iheight+2
                b1(n1,n2)=IntRange(0,2)
                b2(n1,n2)=IntRange(0,2)
            Next
        Next
    End If
    'Screenlock
	For y As Integer= 1 To _iheight
		For x As Integer= 1 To _iwidth
			If B1(x,y)=0 And N1>Rnd Then B2(x,y)=1
			If B1(x,y)=2 Then B2(x,y)=0
			If B1(x,y)=1 Then
				B2(x,y) = 1
				If B1(x-1,y-1)=2 Or B1(x,y-1)=2 Or B1(x+1,y-1)=2 Then B2(x,y)=2
				If B1(x-1,y)=2 Or B1(x+1,y)=2 Or N2>Rnd Then B2(x,y)=2
				If B1(x-1,y+1)=2 Or B1(x,y+1)=2 Or B1(x+1,y+1)=2 Then B2(x,y)=2
            End If
			If B2(x,y)=0 Then:ppset((x-1),(y-1),col1):End If
			If B2(x,y)=1 Then:ppset((x-1),(y-1),col2):End If
			If B2(x,y)=2 Then:ppset((x-1),(y-1),col3):End If
        Next x
    Next y
    water=im
    'Screenunlock
	Copy()
End Function

Sub rotate(im As Any Ptr,angle As Single,shiftx As Integer=0,shifty As Integer=0)
    #define InRange() resultx>=0 And resultx<ddx And resulty>=0 And resulty<ddy And _
    x+shiftx>=0 And x+shiftx<xres And y+shifty>=0 And y+shifty<yres
    Dim As Integer pitch,pitchs,xres,yres
    Dim As Any Ptr row
    Dim As Uinteger Ptr pixel,pixels
    Dim As Integer ddx,ddy
    Imageinfo im,ddx,ddy,,pitch,row
    Screeninfo xres,yres,,,pitchS
    Dim As Any Ptr rowS=Screenptr
    Dim As Integer centreX=ddx\2,centreY=ddy\2
    Dim As Single sx=Sin(angle)
    Dim As Single cx=Cos(angle)
    Dim As Integer mx=Iif(ddx>=ddy,ddx,ddy)
    Var fx=Sqr(2)/2
    For y As Integer=centrey-fx*mx To centrey+ fx*mx Step 1
        For x As Integer=centrex-mx*fx To centrex+mx*fx Step 1
            Dim As Integer resultx=(Cx*(x-centrex)-Sx*(y-centrey)) +centrex,resulty=(Sx*(x-centrex)+Cx*(y-centrey)) +centrey
            If InRange() Then
                pixel=row+pitch*((resulty))+((resultx)) Shl 2
                pixels=rowS+pitchS*(y+shifty)+(x+shiftx) Shl 2
                *pixels=*pixel
            End If
        Next x
    Next y
End Sub

Function Regulate(Byval MyFps As Long,Byref fps As Long) As Long
    Static As Double timervalue,lastsleeptime,t3,frames
    Var t=Timer
    frames+=1
    If (t-t3)>=1 Then t3=t:fps=frames:frames=0
    Var sleeptime=lastsleeptime+((1/myfps)-T+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    lastsleeptime=sleeptime
    timervalue=T
    Return sleeptime
End Function

Dim As Long ctr,fps
'setup water and image
water(120,120)'water image size
Dim As Any Ptr Image=Imagecreate(120,120,Rgb(255,255,255))'same as water image
Dim As Ulong Ptr pi:Dim As Integer size
Imageinfo image,,,,,pi,size
Dim As Any Ptr trn=Imagecreate(xres,yres)
'=============================

Type pt
    As Long x,y
End Type

function spline(p() As Pt,t As Single) As Pt
    #macro set(n)
    0.5 *(     (2 * P(2).n) +_
   (-1*P(1).n + P(3).n) * t +_
    (2*P(1).n - 5*P(2).n + 4*P(3).n - P(4).n) * t*t +_
    (-1*P(1).n + 3*P(2).n- 3*P(3).n + P(4).n) * t*t*t)
    #endmacro
    return type<pt>(set(x),set(y))',set(z))
end function

Sub GetCatmull(v() As Pt,outarray() As Pt,colour as uinteger,arraysize As Integer=1000)
    Dim As Pt p(1 To 4)
    Redim outarray(0)
    Dim As Single stepsize=(Ubound(v)-1)/(arraysize)
    If stepsize>1 Then stepsize=1
    For n As Integer=lbound(v)+1 To Ubound(v)-2
        p(1)=v(n-1):p(2)=v(n):p(3)=v(n+1):p(4)=v(n+2)
        For t As Single=0 To 1 Step stepsize
            Redim Preserve outarray(1 To Ubound(outarray)+1)
            outarray(Ubound(outarray))=spline(p(),t)
        Next t
    Next n
End Sub 

Sub DrawCurve(a() As Pt,i as any ptr=0)
    Pset i,(a(Lbound(a)).x,a(Lbound(a)).y),rgb(255,255,255)
    For z As Integer=Lbound(a)+1 To Ubound(a)
        Line i,-(a(z).x,a(z).y),rgb(255,255,255)
    Next z
End Sub
'get the river bank points from the data
Dim As pt P1(0 To 24)
Dim As pt P2(0 To 24)
For n As Long=1 To 24
    Read P1(n).x
Next
For n As Long=1 To 24
    Read P2(n).x
Next
Restore Y_values
For n As Long=1 To 24
    Read P1(n).y
Next
For n As Long=1 To 24
    Read P2(n).y
Next
'add an extra point for the spline
P1(0).x=-20:P1(0).y=P1(1).y
P2(0).x=-20:P2(0).y=P2(1).y
redim as pt P1C(0),P2c(0)
getcatmull(P1(),P1c(),500)
getcatmull(P2(),P2c(),500)
'draw the splines to image trn
drawcurve(P1c(),trn)
drawcurve(P2c(),trn)

'For n As Long=1 To 24-1
    'Line trn,(P1(n).x,P1(n).y)-(P1(n+1).x,P1(n+1).y),Rgb(255,255,255)
    'Line trn,(P2(n).x,P2(n).y)-(P2(n+1).x,P2(n+1).y),Rgb(255,255,255)
'Next
Paint trn,(900,100),Rgb(0,100,0),Rgb(255,255,255)
Paint trn,(100,600),Rgb(0,100,0),Rgb(255,255,255)
'====================================================


Do
    ctr+=1
    '=================  Water to one image only ===========
    If ctr Mod 4=0 Then Put image,(0,0),water,Alpha:ctr=0
    If ctr Mod 2 =0 Then :Flow(pi):End If
    '===============================================  
    Screenlock
    'Cls  'optional
    'now slide the image along the riverbank, rotating round the bends
    For n As Long=1 To 24-1
        Var a=Atan2((p1(n+1).y-p1(n).y),(p1(n+1).x-p1(n).x))
        If n<16 Then rotate(image,-a,p1(n).x-50,p1(n).y-50)
        If n>=16 And n<=19 Then rotate(image,-a,p2(n).x-20,p2(n).y-50)
        If n>19  Then rotate(image,-a,p1(n).x-65,p1(n).y-50)
    Next
    Put(0,0),trn,trans  'background
    Locate 1,1
    Print fps
    Screenunlock
    Sleep regulate(50,fps),1
Loop Until Len(Inkey)

 Imagedestroy image
 Imagedestroy trn
screen 0
end
X_values:

Data _
0, 114, 176, 237, 247, 300, 360, 447, 512, 568, 591, 628, 688, 788, 838, 859, 853, 839, 807, 801, 888, 960, 1063, 1150, 0, 98, 143, 187, 195, 245, 326, 446, 532, 595, 622, 654, 688, 769, 798, 796, 790, 776, 736, 738, 830, 951, 1063, 1151 

Y_values:

Data _
100, 118, 147, 224, 282, 342, 370, 366, 337, 279, 219, 150, 117, 135, 222, 316, 372, 439, 535, 595, 664, 695, 712, 721, 154, 160, 181, 231, 282, 351, 398, 406, 373, 295, 226, 172, 142, 150, 224, 317, 364, 429, 518, 599, 687, 733, 760, 770 



 
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Wonderful 2D Water effects...

Post by BasicCoder2 »

leopardpm wrote:I like the way this Tower Defense pathing is going - I will code up my ideas for analyzing a given map and determining the influence maps for each of the pathing lanes and make a new thread - maybe it will turn into a little game at some point! It would be nice to be able to take any given handdrawn map and have the program be able to figure out any and all pathing within it.
Ok. I have already started working on it myself with little agents in mortal combat so we can compare notes.
.
Post Reply